blob: 94f172c1d32750e1e669dac56546ab9badcc5db2 [file] [log] [blame]
/*
*
* Copyright (c) 2020-2021 Project CHIP Authors
* Copyright (c) 2013-2017 Nest Labs, Inc.
* 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.
*/
/**
* @file
* Declaration of Commissioner Discovery Controller,
* a common class that manages state and callbacks
* for handling the Commissioner Discovery
* and User Directed Commissioning workflow
*
*/
#pragma once
#include <app/OperationalSessionSetup.h>
#include <lib/core/CHIPConfig.h>
#include <lib/core/CHIPError.h>
#include <lib/core/NodeId.h>
#include <platform/CHIPDeviceLayer.h>
#include <platform/PlatformManager.h>
#include <protocols/user_directed_commissioning/UserDirectedCommissioning.h>
#if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY
using chip::NodeId;
using chip::OperationalSessionSetup;
using chip::Protocols::UserDirectedCommissioning::UDCClientState;
using chip::Protocols::UserDirectedCommissioning::UserConfirmationProvider;
using chip::Protocols::UserDirectedCommissioning::UserDirectedCommissioningServer;
using chip::Transport::PeerAddress;
class DLL_EXPORT UserPrompter
{
public:
/**
* @brief
* Called to prompt the user for consent to allow the given commissioneeName/vendorId/productId to be commissioned.
* For example "[commissioneeName] is requesting permission to cast to this TV, approve?"
*
* If user responds with OK then implementor should call CommissionerRespondOk();
* If user responds with Cancel then implementor should call CommissionerRespondCancel();
*
* @param[in] vendorId The vendorId in the DNS-SD advertisement of the requesting commissionee.
* @param[in] productId The productId in the DNS-SD advertisement of the requesting commissionee.
* @param[in] commissioneeName The commissioneeName in the DNS-SD advertisement of the requesting commissionee.
*
*/
virtual void PromptForCommissionOKPermission(uint16_t vendorId, uint16_t productId, const char * commissioneeName) = 0;
/**
* @brief
* Called to prompt the user to enter the setup passcode displayed by the given commissioneeName/vendorId/productId to be
* commissioned. For example "Please enter passcode displayed in casting app."
*
* If user enters passcode then implementor should call CommissionerRespondPasscode(uint32_t passcode);
* If user responds with Cancel then implementor should call CommissionerRespondCancel();
*
* @param[in] vendorId The vendorId in the DNS-SD advertisement of the requesting commissionee.
* @param[in] productId The productId in the DNS-SD advertisement of the requesting commissionee.
* @param[in] commissioneeName The commissioneeName in the DNS-SD advertisement of the requesting commissionee.
* @param[in] pairingHint The pairingHint in the DNS-SD advertisement of the requesting commissionee.
* @param[in] pairingInstruction The pairingInstruction in the DNS-SD advertisement of the requesting commissionee.
*
*/
virtual void PromptForCommissionPasscode(uint16_t vendorId, uint16_t productId, const char * commissioneeName,
uint16_t pairingHint, const char * pairingInstruction) = 0;
/**
* @brief
* Called to when CancelCommissioning is received via UDC.
* Indicates that commissioner can stop showing the passcode entry or display dialog.
* For example, can show text such as "Commissioning cancelled by client" before hiding dialog.
*
* @param[in] vendorId The vendorId in the DNS-SD advertisement of the requesting commissionee.
* @param[in] productId The productId in the DNS-SD advertisement of the requesting commissionee.
* @param[in] commissioneeName The commissioneeName in the DNS-SD advertisement of the requesting commissionee.
*
*/
virtual void HidePromptsOnCancel(uint16_t vendorId, uint16_t productId, const char * commissioneeName) = 0;
/**
* @brief
* Return true if this UserPrompter displays QR code along with passcode
* When PromptWithCommissionerPasscode is called during Commissioner Passcode functionality.
*/
virtual bool DisplaysPasscodeAndQRCode() = 0;
/**
* @brief
* Called to display the given setup passcode to the user,
* for commissioning the given commissioneeName with the given vendorId and productId,
* and provide instructions for where to enter it in the commissionee (when pairingHint and pairingInstruction are provided).
* For example "Casting Passcode: [passcode]. For more instructions, click here."
*
* @param[in] vendorId The vendorId in the DNS-SD advertisement of the requesting commissionee.
* @param[in] productId The productId in the DNS-SD advertisement of the requesting commissionee.
* @param[in] commissioneeName The commissioneeName in the DNS-SD advertisement of the requesting commissionee.
* @param[in] passcode The passcode to display.
* @param[in] pairingHint The pairingHint in the DNS-SD advertisement of the requesting commissionee.
* @param[in] pairingInstruction The pairingInstruction in the DNS-SD advertisement of the requesting commissionee.
*
*/
virtual void PromptWithCommissionerPasscode(uint16_t vendorId, uint16_t productId, const char * commissioneeName,
uint32_t passcode, uint16_t pairingHint, const char * pairingInstruction) = 0;
/**
* @brief
* Called to alert the user that commissioning has begun."
*
* @param[in] vendorId The vendorid from the DAC of the new node.
* @param[in] productId The productid from the DAC of the new node.
* @param[in] commissioneeName The commissioneeName in the DNS-SD advertisement of the requesting commissionee.
*
*/
virtual void PromptCommissioningStarted(uint16_t vendorId, uint16_t productId, const char * commissioneeName) = 0;
/**
* @brief
* Called to prompt the user that commissioning and post-commissioning steps have completed successfully."
*
* @param[in] vendorId The vendorid from the DAC of the new node.
* @param[in] productId The productid from the DAC of the new node.
* @param[in] commissioneeName The commissioneeName in the DNS-SD advertisement of the requesting commissionee.
*
*/
virtual void PromptCommissioningSucceeded(uint16_t vendorId, uint16_t productId, const char * commissioneeName) = 0;
/**
* @brief
* Called to prompt the user that commissioning and post-commissioning steps have failed."
*
* @param[in] commissioneeName The commissioneeName in the DNS-SD advertisement of the requesting commissionee.
*
*/
virtual void PromptCommissioningFailed(const char * commissioneeName, CHIP_ERROR error) = 0;
virtual ~UserPrompter() = default;
};
class DLL_EXPORT PasscodeService
{
public:
/**
* @brief
* Called to determine if the given target app is available to the commissionee with the given
* vendorId/productId, and if so, return the passcode.
*
* This will be called by the main chip thread so any blocking work should be moved to a separate thread.
*
* After lookup and attempting to obtain the passcode, implementor should call HandleContentAppCheck();
*
* @param[in] vendorId The vendorId in the DNS-SD advertisement of the requesting commissionee.
* @param[in] productId The productId in the DNS-SD advertisement of the requesting commissionee.
* @param[in] rotatingId The rotatingId in the DNS-SD advertisement of the requesting commissionee.
* @param[in] info App info to look for.
*
*/
virtual void LookupTargetContentApp(uint16_t vendorId, uint16_t productId, chip::CharSpan rotatingId,
chip::Protocols::UserDirectedCommissioning::TargetAppInfo & info) = 0;
/**
* @brief
* Called to get the commissioner-generated setup passcode.
* Returns 0 if feature is disabled.
*
* @param[in] vendorId The vendorId in the DNS-SD advertisement of the requesting commissionee.
* @param[in] productId The productId in the DNS-SD advertisement of the requesting commissionee.
* @param[in] rotatingId The rotatingId in the DNS-SD advertisement of the requesting commissionee.
*
*/
virtual uint32_t GetCommissionerPasscode(uint16_t vendorId, uint16_t productId, chip::CharSpan rotatingId) = 0;
/**
* @brief
* Called to get the setup passcode from the content app corresponding to the given vendorId/productId.
*
* This will be called by the main chip thread so any blocking work should be moved to a separate thread.
*
* After attempting to obtain the passcode, implementor should call HandleContentAppPasscodeResponse();
*
* @param[in] vendorId The vendorId in the DNS-SD advertisement of the requesting commissionee.
* @param[in] productId The productId in the DNS-SD advertisement of the requesting commissionee.
* @param[in] rotatingId The rotatingId in the DNS-SD advertisement of the requesting commissionee.
*
*/
virtual void FetchCommissionPasscodeFromContentApp(uint16_t vendorId, uint16_t productId, chip::CharSpan rotatingId) = 0;
virtual ~PasscodeService() = default;
};
class DLL_EXPORT AppInstallationService
{
public:
/**
* @brief
* Called to check if the given target app is available to the commissione with th given
* vendorId/productId
*
* @param[in] vendorId The vendorId in the DNS-SD advertisement of the requesting commissionee.
* @param[in] productId The productId in the DNS-SD advertisement of the requesting commissionee.
*
*/
virtual bool LookupTargetContentApp(uint16_t vendorId, uint16_t productId) = 0;
virtual ~AppInstallationService() = default;
};
class DLL_EXPORT PostCommissioningListener
{
public:
/**
* @brief
* Called to when commissioning completed to allow the listener to perform additional
* steps such as binding and ACL creation.
*
* @param[in] vendorId The vendorid from the DAC of the new node.
* @param[in] productId The productid from the DAC of the new node.
* @param[in] nodeId The node id for the newly commissioned node.
* @param[in] rotatingId The rotating ID to handle account login.
* @param[in] passcode The passcode to handle account login.
* @param[in] exchangeMgr The exchange manager to be used to get an exchange context.
* @param[in] sessionHandle A reference to an established session.
*
*/
virtual void CommissioningCompleted(uint16_t vendorId, uint16_t productId, NodeId nodeId, chip::CharSpan rotatingId,
uint32_t passcode, chip::Messaging::ExchangeManager & exchangeMgr,
const chip::SessionHandle & sessionHandle) = 0;
virtual ~PostCommissioningListener() = default;
};
class DLL_EXPORT CommissionerCallback
{
public:
/**
* @brief
* Called to notify the commissioner that commissioning can now proceed for
* the node identified by the given arguments.
*
* @param[in] passcode The passcode to use for the commissionee.
* @param[in] longDiscriminator The long discriminator for the commissionee.
* @param[in] peerAddress The peerAddress for the commissionee.
*
*/
virtual void ReadyForCommissioning(uint32_t passcode, uint16_t longDiscriminator, PeerAddress peerAddress) = 0;
virtual ~CommissionerCallback() = default;
};
class CommissionerDiscoveryController : public chip::Protocols::UserDirectedCommissioning::UserConfirmationProvider
{
public:
/**
* This controller can only handle one outstanding UDC session at a time and will
* reject attempts to start a second when one is outstanding.
*
* A session ends when post-commissioning completes:
* - PostCommissioningSucceeded()
* or when one of the following failure methods is called:
* - PostCommissioningFailed()
* - CommissioningFailed()
*
* Reset the state of this controller so that a new sessions will be accepted.
*/
void ResetState();
/**
* Check whether we have a valid session (and reset state if not).
*/
void ValidateSession();
/**
* UserConfirmationProvider callback.
*
* Notification that a UDC protocol message was received.
*
* This code will call the registered UserPrompter's PromptForCommissionOKPermission
*/
void OnUserDirectedCommissioningRequest(UDCClientState state) override;
/**
* UserConfirmationProvider callback.
*
* Notification that a Cancel UDC protocol message was received.
*
* This code will call the registered UserPrompter's HidePromptsOnCancel
*/
void OnCancel(UDCClientState state) override;
/**
* UserConfirmationProvider callback.
*
* Notification that a CommissionerPasscodeReady UDC protocol message was received.
*
* This code will trigger the Commissioner to begin commissioning
*/
void OnCommissionerPasscodeReady(UDCClientState state) override;
/**
* This method should be called after the user has given consent for commissioning of the client
* indicated in the UserPrompter's PromptForCommissionOKPermission callback
*/
void Ok();
void InternalOk();
/**
* This method should be called after the user has declined to give consent for commissioning of the client
* indicated in the UserPrompter's PromptForCommissionOKPermission callback
*/
void Cancel();
/**
* @brief
* Called with the result of attempting to obtain the passcode from the content app corresponding to the given
* vendorId/productId.
*
* @param[in] passcode Passcode for the given commissionee, or 0 if passcode cannot be obtained.
*
*/
void HandleContentAppPasscodeResponse(uint32_t passcode);
void InternalHandleContentAppPasscodeResponse();
/**
* Cache the passcode to use for commissioning
*/
inline void SetPasscode(uint32_t passcode) { mPasscode = passcode; }
/**
* @brief
* Called with the result of attempting to lookup and obtain the passcode from the content app corresponding to the given
* target.
*
* @param[in] target Target app info for app check.
* @param[in] passcode Passcode for the given commissionee, or 0 if passcode cannot be obtained.
*
*/
void HandleTargetContentAppCheck(chip::Protocols::UserDirectedCommissioning::TargetAppInfo target, uint32_t passcode);
/**
* This method should be called with the passcode for the client
* indicated in the UserPrompter's PromptForCommissionPasscode callback
*/
void CommissionWithPasscode(uint32_t passcode);
void InternalCommissionWithPasscode();
/**
* This method should be called by the commissioner to indicate that commissioning succeeded.
* The PostCommissioningCallback will then be invoked to complete setup
*
* @param[in] vendorId The vendorid from the DAC of the new node.
* @param[in] productId The productid from the DAC of the new node.
* @param[in] nodeId The node id for the newly commissioned node.
* @param[in] exchangeMgr The exchange manager to be used to get an exchange context.
* @param[in] sessionHandle A reference to an established session.
*
*/
void CommissioningSucceeded(uint16_t vendorId, uint16_t productId, NodeId nodeId,
chip::Messaging::ExchangeManager & exchangeMgr, const chip::SessionHandle & sessionHandle);
/**
* This method should be called by the commissioner to indicate that commissioning failed.
* The UserPrompter will then be invoked to notify the user of the failure.
*/
void CommissioningFailed(CHIP_ERROR error);
/**
* This method should be called by the PostCommissioningListener to indicate that post-commissioning steps completed.
* The PromptCommissioningSucceeded will then be invoked to notify the user of success.
*/
void PostCommissioningSucceeded();
/**
* This method should be called by the PostCommissioningListener to indicate that post-commissioning steps failed.
* The PromptCommissioningFailed will then be invoked to notify the user of failure.
*/
void PostCommissioningFailed(CHIP_ERROR error);
/**
* Assign a DeviceCommissioner
*/
inline void SetUserDirectedCommissioningServer(UserDirectedCommissioningServer * udcServer)
{
mUdcServer = udcServer;
mUdcServer->SetUserConfirmationProvider(this);
}
/**
* Assign a UserPromper
*/
inline void SetUserPrompter(UserPrompter * userPrompter) { mUserPrompter = userPrompter; }
/**
* Assign a PasscodeService
*/
inline void SetPasscodeService(PasscodeService * passcodeService) { mPasscodeService = passcodeService; }
inline PasscodeService * GetPasscodeService() { return mPasscodeService; }
/**
* Assign an AppInstallationService
*/
inline void SetAppInstallationService(AppInstallationService * appInstallationService)
{
mAppInstallationService = appInstallationService;
}
/**
* Assign a Commissioner Callback to perform commissioning once user consent has been given
*/
inline void SetCommissionerCallback(CommissionerCallback * commissionerCallback)
{
mCommissionerCallback = commissionerCallback;
}
/**
* Assign a PostCommissioning Listener to perform post-commissioning operations
*/
inline void SetPostCommissioningListener(PostCommissioningListener * postCommissioningListener)
{
mPostCommissioningListener = postCommissioningListener;
}
/**
* Get the commissioneeName in the DNS-SD advertisement of the requesting commissionee.
*/
const char * GetCommissioneeName();
/**
* Get the UDCClientState of the requesting commissionee.
*/
UDCClientState * GetUDCClientState();
protected:
bool mReady = true; // ready to start commissioning
bool mPendingConsent = false;
char mCurrentInstance[chip::Dnssd::Commission::kInstanceNameMaxLength + 1];
uint16_t mVendorId = 0;
uint16_t mProductId = 0;
NodeId mNodeId = 0;
uint32_t mPasscode = 0;
std::string mRotatingId;
UserDirectedCommissioningServer * mUdcServer = nullptr;
UserPrompter * mUserPrompter = nullptr;
PasscodeService * mPasscodeService = nullptr;
AppInstallationService * mAppInstallationService = nullptr;
CommissionerCallback * mCommissionerCallback = nullptr;
PostCommissioningListener * mPostCommissioningListener = nullptr;
};
#endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY