/* * * 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 #include #include #include #include #include #include #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