diff --git a/examples/platform/linux/CommissioneeShellCommands.cpp b/examples/platform/linux/CommissioneeShellCommands.cpp index f04051b36e8f88..18f8355080201d 100644 --- a/examples/platform/linux/CommissioneeShellCommands.cpp +++ b/examples/platform/linux/CommissioneeShellCommands.cpp @@ -37,7 +37,8 @@ namespace chip { namespace Shell { #if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT -static CHIP_ERROR SendUDC(bool printHeader, chip::Transport::PeerAddress commissioner) +static CHIP_ERROR SendUDC(bool printHeader, chip::Transport::PeerAddress commissioner, + Protocols::UserDirectedCommissioning::IdentificationDeclaration id) { streamer_t * sout = streamer_get(); @@ -46,7 +47,7 @@ static CHIP_ERROR SendUDC(bool printHeader, chip::Transport::PeerAddress commiss streamer_printf(sout, "SendUDC: "); } - Server::GetInstance().SendUserDirectedCommissioningRequest(commissioner); + Server::GetInstance().SendUserDirectedCommissioningRequest(commissioner, id); streamer_printf(sout, "done\r\n"); @@ -60,6 +61,13 @@ static CHIP_ERROR PrintAllCommands() streamer_printf(sout, " help Usage: commissionee \r\n"); #if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT streamer_printf(sout, " sendudc
Send UDC message to address. Usage: commissionee sendudc ::1 5543\r\n"); + streamer_printf(sout, " udccancel
Send UDC cancel message to address. Usage: cast udccancel ::1 5543\r\n"); + streamer_printf(sout, + " udccommissionerpasscode
[CommissionerPasscodeReady] [PairingHint] [PairingInst] Send UDC " + "commissioner passcode message to address. Usage: udccommissionerpasscode ::1 5543 t 5 HelloWorld\r\n"); + streamer_printf(sout, + " testudc
[NoPasscode] [CdUponPasscodeDialog] [vid] [PairingHint] [PairingInst] Send UDC " + "message to address. Usage: cast testudc ::1 5543 t t 5 HelloWorld\r\n"); #endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT // TODO: Figure out whether setdiscoverytimeout is a reasonable thing to do // at all, and if so what semantics it should have. Presumably it should @@ -92,12 +100,93 @@ static CHIP_ERROR CommissioneeHandler(int argc, char ** argv) } #if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT if (strcmp(argv[0], "sendudc") == 0) + { + Protocols::UserDirectedCommissioning::IdentificationDeclaration id; + + char * eptr; + chip::Inet::IPAddress commissioner; + chip::Inet::IPAddress::FromString(argv[1], commissioner); + uint16_t port = (uint16_t) strtol(argv[2], &eptr, 10); + + return SendUDC(true, chip::Transport::PeerAddress::UDP(commissioner, port), id); + } + if (strcmp(argv[0], "udccancel") == 0) + { + char * eptr; + chip::Inet::IPAddress commissioner; + chip::Inet::IPAddress::FromString(argv[1], commissioner); + uint16_t port = (uint16_t) strtol(argv[2], &eptr, 10); + + Protocols::UserDirectedCommissioning::IdentificationDeclaration id; + id.SetCancelPasscode(true); + return Server::GetInstance().SendUserDirectedCommissioningRequest(chip::Transport::PeerAddress::UDP(commissioner, port), + id); + } + if (strcmp(argv[0], "udccommissionerpasscode") == 0) { char * eptr; chip::Inet::IPAddress commissioner; chip::Inet::IPAddress::FromString(argv[1], commissioner); uint16_t port = (uint16_t) strtol(argv[2], &eptr, 10); - return SendUDC(true, chip::Transport::PeerAddress::UDP(commissioner, port)); + + // udccommissionerpasscode
[CommissionerPasscodeReady] [PairingHint] [PairingInst] + // ex. udccommissionerpasscode
t 5 'hello world' + + Protocols::UserDirectedCommissioning::IdentificationDeclaration id; + id.SetCommissionerPasscode(true); + if (argc > 3) + { + id.SetCommissionerPasscodeReady(strcmp(argv[3], "t") == 0); + } + if (argc > 4) + { + uint16_t hint = (uint16_t) strtol(argv[4], &eptr, 10); + id.SetPairingHint(hint); + } + if (argc > 5) + { + id.SetPairingInst(argv[5]); + } + return Server::GetInstance().SendUserDirectedCommissioningRequest(chip::Transport::PeerAddress::UDP(commissioner, port), + id); + } + if (strcmp(argv[0], "testudc") == 0) + { + char * eptr; + chip::Inet::IPAddress commissioner; + chip::Inet::IPAddress::FromString(argv[1], commissioner); + uint16_t port = (uint16_t) strtol(argv[2], &eptr, 10); + + // sendudc
[NoPasscode] [CdUponPasscodeDialog] [vid] [PairingHint] [PairingInst] + // ex. sendudc
t t 111 5 'hello world' + + Protocols::UserDirectedCommissioning::IdentificationDeclaration id; + if (argc > 3) + { + id.SetNoPasscode(strcmp(argv[3], "t") == 0); + } + if (argc > 4) + { + id.SetCdUponPasscodeDialog(strcmp(argv[4], "t") == 0); + } + if (argc > 5) + { + uint16_t vid = (uint16_t) strtol(argv[5], &eptr, 10); + Protocols::UserDirectedCommissioning::TargetAppInfo info; + info.vendorId = vid; + id.AddTargetAppInfo(info); + } + if (argc > 6) + { + uint16_t hint = (uint16_t) strtol(argv[6], &eptr, 10); + id.SetPairingHint(hint); + } + if (argc > 7) + { + id.SetPairingInst(argv[7]); + } + return Server::GetInstance().SendUserDirectedCommissioningRequest(chip::Transport::PeerAddress::UDP(commissioner, port), + id); } #endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT // TODO: Figure out whether setdiscoverytimeout is a reasonable thing to do diff --git a/examples/platform/linux/ControllerShellCommands.cpp b/examples/platform/linux/ControllerShellCommands.cpp index 3d5af4a3a387a1..27116b28c15256 100644 --- a/examples/platform/linux/ControllerShellCommands.cpp +++ b/examples/platform/linux/ControllerShellCommands.cpp @@ -266,8 +266,8 @@ static CHIP_ERROR ControllerHandler(int argc, char ** argv) { if (argc >= 3) { - uint32_t pincode = (uint32_t) strtol(argv[2], &eptr, 10); - GetCommissionerDiscoveryController()->CommissionWithPincode(pincode); + uint32_t passcode = (uint32_t) strtol(argv[2], &eptr, 10); + GetCommissionerDiscoveryController()->CommissionWithPasscode(passcode); return CHIP_NO_ERROR; } GetCommissionerDiscoveryController()->Ok(); @@ -280,15 +280,15 @@ static CHIP_ERROR ControllerHandler(int argc, char ** argv) } else if (strcmp(argv[0], "udc-commission") == 0) { - // udc-commission pincode index + // udc-commission passcode index if (argc < 3) { return PrintAllCommands(); } char * eptr; - uint32_t pincode = (uint32_t) strtol(argv[1], &eptr, 10); - size_t index = (size_t) strtol(argv[2], &eptr, 10); - return pairUDC(true, pincode, index); + uint32_t passcode = (uint32_t) strtol(argv[1], &eptr, 10); + size_t index = (size_t) strtol(argv[2], &eptr, 10); + return pairUDC(true, passcode, index); } #endif // CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE diff --git a/examples/tv-app/android/java/MyUserPrompter-JNI.cpp b/examples/tv-app/android/java/MyUserPrompter-JNI.cpp index 29fff989fdff57..3d29126e5b838a 100644 --- a/examples/tv-app/android/java/MyUserPrompter-JNI.cpp +++ b/examples/tv-app/android/java/MyUserPrompter-JNI.cpp @@ -116,7 +116,8 @@ void JNIMyUserPrompter::PromptForCommissionOKPermission(uint16_t vendorId, uint1 * If user responds with Cancel then implementor calls UserPrompterResolver.OnPinCodeDeclined(); * */ -void JNIMyUserPrompter::PromptForCommissionPincode(uint16_t vendorId, uint16_t productId, const char * commissioneeName) +void JNIMyUserPrompter::PromptForCommissionPasscode(uint16_t vendorId, uint16_t productId, const char * commissioneeName, + uint16_t pairingHint, const char * pairingInstruction) { CHIP_ERROR err = CHIP_NO_ERROR; JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); @@ -148,6 +149,50 @@ void JNIMyUserPrompter::PromptForCommissionPincode(uint16_t vendorId, uint16_t p } } +/** + * 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. + */ +void JNIMyUserPrompter::HidePromptsOnCancel(uint16_t vendorId, uint16_t productId, const char * commissioneeName) +{ + // TODO + ChipLogError(Zcl, "JNIMyUserPrompter::HidePromptsOnCancel Needs Implementation"); +} + +/** + * Return true if this UserPrompter displays QR code along with passcode + * When PromptWithCommissionerPasscode is called during Commissioner Passcode functionality. + */ +bool JNIMyUserPrompter::DisplaysPasscodeAndQRCode() +{ + // TODO + ChipLogError(Zcl, "JNIMyUserPrompter::DisplaysPasscodeAndQRCode Needs Implementation"); + return false; +} + +/** + * 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." + */ +void JNIMyUserPrompter::PromptWithCommissionerPasscode(uint16_t vendorId, uint16_t productId, const char * commissioneeName, + uint32_t passcode, uint16_t pairingHint, const char * pairingInstruction) +{ + // TODO + ChipLogError(Zcl, "JNIMyUserPrompter::PromptWithCommissionerPasscode Needs Implementation"); +} + +/** + * Called to alert the user that commissioning has begun." + */ +void JNIMyUserPrompter::PromptCommissioningStarted(uint16_t vendorId, uint16_t productId, const char * commissioneeName) +{ + // TODO + ChipLogError(Zcl, "JNIMyUserPrompter::PromptCommissioningStarted Needs Implementation"); +} + /* * Called to notify the user that commissioning succeeded. It can be in form of UI Notification. */ diff --git a/examples/tv-app/android/java/MyUserPrompter-JNI.h b/examples/tv-app/android/java/MyUserPrompter-JNI.h index cecdcb305643d2..f4d93ad49b10e1 100644 --- a/examples/tv-app/android/java/MyUserPrompter-JNI.h +++ b/examples/tv-app/android/java/MyUserPrompter-JNI.h @@ -23,9 +23,16 @@ class JNIMyUserPrompter : public UserPrompter { public: + // TODO JNIMyUserPrompter(jobject prompter); void PromptForCommissionOKPermission(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override; - void PromptForCommissionPincode(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override; + void PromptForCommissionPasscode(uint16_t vendorId, uint16_t productId, const char * commissioneeName, uint16_t pairingHint, + const char * pairingInstruction) override; + void HidePromptsOnCancel(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override; + bool DisplaysPasscodeAndQRCode() override; + void PromptWithCommissionerPasscode(uint16_t vendorId, uint16_t productId, const char * commissioneeName, uint32_t passcode, + uint16_t pairingHint, const char * pairingInstruction) override; + void PromptCommissioningStarted(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override; void PromptCommissioningSucceeded(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override; void PromptCommissioningFailed(const char * commissioneeName, CHIP_ERROR error) override; diff --git a/examples/tv-app/android/java/MyUserPrompterResolver-JNI.cpp b/examples/tv-app/android/java/MyUserPrompterResolver-JNI.cpp index ebe342da2f4a43..2e6bd0aa1f54e3 100644 --- a/examples/tv-app/android/java/MyUserPrompterResolver-JNI.cpp +++ b/examples/tv-app/android/java/MyUserPrompterResolver-JNI.cpp @@ -34,7 +34,7 @@ JNI_METHOD(void, OnPinCodeEntered)(JNIEnv *, jobject, jint jPinCode) chip::DeviceLayer::StackLock lock; uint32_t pinCode = (uint32_t) jPinCode; ChipLogProgress(Zcl, "OnPinCodeEntered %d", pinCode); - GetCommissionerDiscoveryController()->CommissionWithPincode(pinCode); + GetCommissionerDiscoveryController()->CommissionWithPasscode(pinCode); #endif } diff --git a/examples/tv-app/android/java/TVApp-JNI.cpp b/examples/tv-app/android/java/TVApp-JNI.cpp index d7081419c0c645..f8afdfeb9c3b5f 100644 --- a/examples/tv-app/android/java/TVApp-JNI.cpp +++ b/examples/tv-app/android/java/TVApp-JNI.cpp @@ -189,11 +189,23 @@ JNI_METHOD(void, setChipDeviceEventProvider)(JNIEnv *, jobject, jobject provider } #if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED -class MyPincodeService : public PincodeService +class MyPincodeService : public PasscodeService { - uint32_t FetchCommissionPincodeFromContentApp(uint16_t vendorId, uint16_t productId, CharSpan rotatingId) override + bool HasTargetContentApp(uint16_t vendorId, uint16_t productId, chip::CharSpan rotatingId, + chip::Protocols::UserDirectedCommissioning::TargetAppInfo & info, uint32_t & passcode) override { - return ContentAppPlatform::GetInstance().GetPincodeFromContentApp(vendorId, productId, rotatingId); + return ContentAppPlatform::GetInstance().HasTargetContentApp(vendorId, productId, rotatingId, info, passcode); + } + + uint32_t GetCommissionerPasscode(uint16_t vendorId, uint16_t productId, chip::CharSpan rotatingId) override + { + // TODO: randomly generate this value + return 12345678; + } + + uint32_t FetchCommissionPasscodeFromContentApp(uint16_t vendorId, uint16_t productId, CharSpan rotatingId) override + { + return ContentAppPlatform::GetInstance().GetPasscodeFromContentApp(vendorId, productId, rotatingId); } }; MyPincodeService gMyPincodeService; @@ -329,7 +341,7 @@ void TvAppJNI::InitializeCommissioner(JNIMyUserPrompter * userPrompter) CommissionerDiscoveryController * cdc = GetCommissionerDiscoveryController(); if (cdc != nullptr && userPrompter != nullptr) { - cdc->SetPincodeService(&gMyPincodeService); + cdc->SetPasscodeService(&gMyPincodeService); cdc->SetUserPrompter(userPrompter); cdc->SetPostCommissioningListener(&gMyPostCommissioningListener); } diff --git a/examples/tv-app/tv-common/include/CHIPProjectAppConfig.h b/examples/tv-app/tv-common/include/CHIPProjectAppConfig.h index 99274da0114257..e5ec3f064a2fbf 100644 --- a/examples/tv-app/tv-common/include/CHIPProjectAppConfig.h +++ b/examples/tv-app/tv-common/include/CHIPProjectAppConfig.h @@ -30,6 +30,9 @@ // TVs need to be commissioners and likely want to be discoverable #define CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY 1 +// TVs will often enable this feature +#define CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_PASSCODE 1 + // TVs need to be both commissioners and commissionees #define CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE 1 diff --git a/examples/tv-app/tv-common/src/AppTv.cpp b/examples/tv-app/tv-common/src/AppTv.cpp index ae87badcbcc1e4..8cd0cd409b783a 100644 --- a/examples/tv-app/tv-common/src/AppTv.cpp +++ b/examples/tv-app/tv-common/src/AppTv.cpp @@ -62,7 +62,27 @@ class MyUserPrompter : public UserPrompter } // tv should override this with a dialog prompt - inline void PromptForCommissionPincode(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override + inline void PromptForCommissionPasscode(uint16_t vendorId, uint16_t productId, const char * commissioneeName, + uint16_t pairingHint, const char * pairingInstruction) override + { + return; + } + + // tv should override this with a dialog prompt + inline void HidePromptsOnCancel(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override { return; } + + // set to true when TV displays both QR and Passcode during Commissioner Passcode display. + inline bool DisplaysPasscodeAndQRCode() override { return true; } + + // tv should override this with a dialog prompt + inline void PromptWithCommissionerPasscode(uint16_t vendorId, uint16_t productId, const char * commissioneeName, + uint32_t passcode, uint16_t pairingHint, const char * pairingInstruction) override + { + return; + } + + // tv should override this with a dialog prompt + inline void PromptCommissioningStarted(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override { return; } @@ -79,14 +99,26 @@ class MyUserPrompter : public UserPrompter MyUserPrompter gMyUserPrompter; -class MyPincodeService : public PincodeService +class MyPasscodeService : public PasscodeService { - uint32_t FetchCommissionPincodeFromContentApp(uint16_t vendorId, uint16_t productId, CharSpan rotatingId) override + bool HasTargetContentApp(uint16_t vendorId, uint16_t productId, chip::CharSpan rotatingId, + chip::Protocols::UserDirectedCommissioning::TargetAppInfo & info, uint32_t & passcode) override + { + return ContentAppPlatform::GetInstance().HasTargetContentApp(vendorId, productId, rotatingId, info, passcode); + } + + uint32_t GetCommissionerPasscode(uint16_t vendorId, uint16_t productId, chip::CharSpan rotatingId) override + { + // TODO: randomly generate this value + return 12345678; + } + + uint32_t FetchCommissionPasscodeFromContentApp(uint16_t vendorId, uint16_t productId, CharSpan rotatingId) override { - return ContentAppPlatform::GetInstance().GetPincodeFromContentApp(vendorId, productId, rotatingId); + return ContentAppPlatform::GetInstance().GetPasscodeFromContentApp(vendorId, productId, rotatingId); } }; -MyPincodeService gMyPincodeService; +MyPasscodeService gMyPasscodeService; class MyPostCommissioningListener : public PostCommissioningListener { @@ -558,7 +590,7 @@ CHIP_ERROR AppTvInit() CommissionerDiscoveryController * cdc = GetCommissionerDiscoveryController(); if (cdc != nullptr) { - cdc->SetPincodeService(&gMyPincodeService); + cdc->SetPasscodeService(&gMyPasscodeService); cdc->SetUserPrompter(&gMyUserPrompter); cdc->SetPostCommissioningListener(&gMyPostCommissioningListener); } diff --git a/examples/tv-casting-app/linux/CastingShellCommands.cpp b/examples/tv-casting-app/linux/CastingShellCommands.cpp index 136c0fef46625f..f6b245b42dc13a 100644 --- a/examples/tv-casting-app/linux/CastingShellCommands.cpp +++ b/examples/tv-casting-app/linux/CastingShellCommands.cpp @@ -60,6 +60,13 @@ static CHIP_ERROR PrintAllCommands() sout, " access Read and display clusters on each endpoint for . Usage: cast access 0xFFFFFFEFFFFFFFFF\r\n"); streamer_printf(sout, " sendudc
Send UDC message to address. Usage: cast sendudc ::1 5543\r\n"); + streamer_printf(sout, " udccancel
Send UDC cancel message to address. Usage: cast udccancel ::1 5543\r\n"); + streamer_printf(sout, + " udccommissionerpasscode
[CommissionerPasscodeReady] [PairingHint] [PairingInst] Send UDC " + "commissioner passcode message to address. Usage: udccommissionerpasscode ::1 5543 t 5 HelloWorld\r\n"); + streamer_printf(sout, + " testudc
[NoPasscode] [CdUponPasscodeDialog] [vid] [PairingHint] [PairingInst] Send UDC " + "message to address. Usage: cast testudc ::1 5543 t t 5 HelloWorld\r\n"); streamer_printf( sout, " cluster [clustercommand] Send cluster command. Usage: cast cluster keypadinput send-key 1 18446744004990074879 1\r\n"); @@ -148,6 +155,84 @@ static CHIP_ERROR CastingHandler(int argc, char ** argv) return CastingServer::GetInstance()->SendUserDirectedCommissioningRequest( chip::Transport::PeerAddress::UDP(commissioner, port)); } + if (strcmp(argv[0], "udccancel") == 0) + { + char * eptr; + chip::Inet::IPAddress commissioner; + chip::Inet::IPAddress::FromString(argv[1], commissioner); + uint16_t port = (uint16_t) strtol(argv[2], &eptr, 10); + + Protocols::UserDirectedCommissioning::IdentificationDeclaration id; + id.SetCancelPasscode(true); + return Server::GetInstance().SendUserDirectedCommissioningRequest(chip::Transport::PeerAddress::UDP(commissioner, port), + id); + } + if (strcmp(argv[0], "udccommissionerpasscode") == 0) + { + char * eptr; + chip::Inet::IPAddress commissioner; + chip::Inet::IPAddress::FromString(argv[1], commissioner); + uint16_t port = (uint16_t) strtol(argv[2], &eptr, 10); + + // udccommissionerpasscode
[CommissionerPasscodeReady] [PairingHint] [PairingInst] + // ex. udccommissionerpasscode
t 5 'hello world' + + Protocols::UserDirectedCommissioning::IdentificationDeclaration id; + id.SetCommissionerPasscode(true); + if (argc > 3) + { + id.SetCommissionerPasscodeReady(strcmp(argv[3], "t") == 0); + } + if (argc > 4) + { + uint16_t hint = (uint16_t) strtol(argv[4], &eptr, 10); + id.SetPairingHint(hint); + } + if (argc > 5) + { + id.SetPairingInst(argv[5]); + } + return Server::GetInstance().SendUserDirectedCommissioningRequest(chip::Transport::PeerAddress::UDP(commissioner, port), + id); + } + if (strcmp(argv[0], "testudc") == 0) + { + char * eptr; + chip::Inet::IPAddress commissioner; + chip::Inet::IPAddress::FromString(argv[1], commissioner); + uint16_t port = (uint16_t) strtol(argv[2], &eptr, 10); + + // sendudc
[NoPasscode] [CdUponPasscodeDialog] [vid] [PairingHint] [PairingInst] + // ex. sendudc
t t 111 5 'hello world' + + Protocols::UserDirectedCommissioning::IdentificationDeclaration id; + if (argc > 3) + { + id.SetNoPasscode(strcmp(argv[3], "t") == 0); + } + if (argc > 4) + { + id.SetCdUponPasscodeDialog(strcmp(argv[4], "t") == 0); + } + if (argc > 5) + { + uint16_t vid = (uint16_t) strtol(argv[5], &eptr, 10); + Protocols::UserDirectedCommissioning::TargetAppInfo info; + info.vendorId = vid; + id.AddTargetAppInfo(info); + } + if (argc > 6) + { + uint16_t hint = (uint16_t) strtol(argv[6], &eptr, 10); + id.SetPairingHint(hint); + } + if (argc > 7) + { + id.SetPairingInst(argv[7]); + } + return Server::GetInstance().SendUserDirectedCommissioningRequest(chip::Transport::PeerAddress::UDP(commissioner, port), + id); + } #endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT if (strcmp(argv[0], "print-bindings") == 0) { diff --git a/examples/tv-casting-app/tv-casting-common/BUILD.gn b/examples/tv-casting-app/tv-casting-common/BUILD.gn index d9c391b1e1b037..e8b62bce23969f 100644 --- a/examples/tv-casting-app/tv-casting-common/BUILD.gn +++ b/examples/tv-casting-app/tv-casting-common/BUILD.gn @@ -52,6 +52,8 @@ chip_data_model("tv-casting-common") { "${chip_root}/src/controller/ExamplePersistentStorage.h", "${chip_root}/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp", "${chip_root}/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp", + "clusters/content-app-observer/ContentAppObserver.cpp", + "clusters/content-app-observer/ContentAppObserver.h", "commands/clusters/ModelCommand.cpp", "commands/common/CHIPCommand.cpp", "include/AppParams.h", @@ -89,6 +91,7 @@ chip_data_model("tv-casting-common") { "src/TargetNavigator.cpp", "src/TargetVideoPlayerInfo.cpp", "src/WakeOnLan.cpp", + "src/ZCLCallbacks.cpp", ] # Add simplified casting API files here diff --git a/examples/tv-casting-app/tv-casting-common/clusters/content-app-observer/ContentAppObserver.cpp b/examples/tv-casting-app/tv-casting-common/clusters/content-app-observer/ContentAppObserver.cpp new file mode 100644 index 00000000000000..a6010a54b0a0f5 --- /dev/null +++ b/examples/tv-casting-app/tv-casting-common/clusters/content-app-observer/ContentAppObserver.cpp @@ -0,0 +1,50 @@ +/** + * + * Copyright (c) 2023 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ContentAppObserver.h" + +#include +#include + +using namespace std; +using namespace chip; +using namespace chip::app::Clusters::ContentAppObserver; + +ContentAppObserverManager::ContentAppObserverManager() +{ + // Create Test Data +} + +void ContentAppObserverManager::HandleContentAppMessage(chip::app::CommandResponseHelper & helper, + const chip::Optional & data, + const chip::CharSpan & encodingHint) +{ + ChipLogProgress(Zcl, "ContentAppObserverManager::HandleContentAppMessage"); + + string dataString(data.HasValue() ? data.Value().data() : "", data.HasValue() ? data.Value().size() : 0); + string encodingHintString(encodingHint.data(), encodingHint.size()); + + ChipLogProgress(Zcl, "ContentAppObserverManager::HandleContentAppMessage TEST CASE hint=%s data=%s ", + encodingHintString.c_str(), dataString.c_str()); + + ContentAppMessageResponse response; + // TODO: Insert code here + response.data = chip::MakeOptional(CharSpan::fromCharString("exampleData")); + response.encodingHint = chip::MakeOptional(CharSpan::fromCharString(encodingHintString.c_str())); + response.status = StatusEnum::kSuccess; + helper.Success(response); +} diff --git a/examples/tv-casting-app/tv-casting-common/clusters/content-app-observer/ContentAppObserver.h b/examples/tv-casting-app/tv-casting-common/clusters/content-app-observer/ContentAppObserver.h new file mode 100644 index 00000000000000..0c0f6fb6efbaec --- /dev/null +++ b/examples/tv-casting-app/tv-casting-common/clusters/content-app-observer/ContentAppObserver.h @@ -0,0 +1,37 @@ +/* + * + * 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 +#include +#include + +using ContentAppObserverDelegate = chip::app::Clusters::ContentAppObserver::Delegate; +using ContentAppMessageResponse = chip::app::Clusters::ContentAppObserver::Commands::ContentAppMessageResponse::Type; + +class ContentAppObserverManager : public ContentAppObserverDelegate +{ +public: + ContentAppObserverManager(); + + void HandleContentAppMessage(chip::app::CommandResponseHelper & helper, + const chip::Optional & data, const chip::CharSpan & encodingHint) override; + +protected: +}; diff --git a/examples/tv-casting-app/tv-casting-common/core/CastingPlayer.cpp b/examples/tv-casting-app/tv-casting-common/core/CastingPlayer.cpp index e469775ce27bc0..8ea90c21edb969 100644 --- a/examples/tv-casting-app/tv-casting-common/core/CastingPlayer.cpp +++ b/examples/tv-casting-app/tv-casting-common/core/CastingPlayer.cpp @@ -163,8 +163,10 @@ CHIP_ERROR CastingPlayer::SendUserDirectedCommissioningRequest() ReturnErrorOnFailure(support::ChipDeviceEventHandler::SetUdcStatus(true)); + // TODO: expose options to the higher layer + chip::Protocols::UserDirectedCommissioning::IdentificationDeclaration id; ReturnErrorOnFailure(chip::Server::GetInstance().SendUserDirectedCommissioningRequest( - chip::Transport::PeerAddress::UDP(*ipAddressToUse, mAttributes.port, mAttributes.interfaceId))); + chip::Transport::PeerAddress::UDP(*ipAddressToUse, mAttributes.port, mAttributes.interfaceId), id)); return CHIP_NO_ERROR; } diff --git a/examples/tv-casting-app/tv-casting-common/src/CastingServer.cpp b/examples/tv-casting-app/tv-casting-common/src/CastingServer.cpp index fbbb9d32d8f030..e4bb7647085b40 100644 --- a/examples/tv-casting-app/tv-casting-common/src/CastingServer.cpp +++ b/examples/tv-casting-app/tv-casting-common/src/CastingServer.cpp @@ -191,7 +191,9 @@ void CastingServer::OnCommissioningSessionEstablishmentStarted() #if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT CHIP_ERROR CastingServer::SendUserDirectedCommissioningRequest(chip::Transport::PeerAddress commissioner) { - return Server::GetInstance().SendUserDirectedCommissioningRequest(commissioner); + // TODO: expose options to the higher layer + Protocols::UserDirectedCommissioning::IdentificationDeclaration id; + return Server::GetInstance().SendUserDirectedCommissioningRequest(commissioner, id); } chip::Inet::IPAddress * CastingServer::getIpAddressForUDCRequest(chip::Inet::IPAddress ipAddresses[], const size_t numIPs) diff --git a/examples/tv-casting-app/tv-casting-common/src/ZCLCallbacks.cpp b/examples/tv-casting-app/tv-casting-common/src/ZCLCallbacks.cpp new file mode 100644 index 00000000000000..608d10b4ef5da2 --- /dev/null +++ b/examples/tv-casting-app/tv-casting-common/src/ZCLCallbacks.cpp @@ -0,0 +1,67 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * This file implements the handler for data model messages. + */ + +#include +#include +#include +#include +#include + +#include "clusters/content-app-observer/ContentAppObserver.h" + +namespace { +static ContentAppObserverManager contentAppObserverManager; +} // namespace + +using namespace ::chip; +using namespace ::chip::app::Clusters; + +void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & attributePath, uint8_t type, uint16_t size, + uint8_t * value) +{ + ClusterId clusterId = attributePath.mClusterId; + AttributeId attributeId = attributePath.mAttributeId; + ChipLogProgress(Zcl, "AttributeChangeCallback cluster: " ChipLogFormatMEI " attribute:" ChipLogFormatMEI, + ChipLogValueMEI(clusterId), ChipLogValueMEI(attributeId)); +} + +/** @brief OnOff Cluster Init + * + * This function is called when a specific cluster is initialized. It gives the + * application an opportunity to take care of cluster initialization procedures. + * It is called exactly once for each endpoint where cluster is present. + * + * @param endpoint Ver.: always + * + * TODO Issue #3841 + * emberAfOnOffClusterInitCallback happens before the stack initialize the cluster + * attributes to the default value. + * The logic here expects something similar to the deprecated Plugins callback + * emberAfPluginOnOffClusterServerPostInitCallback. + * + */ + +void emberAfContentAppObserverClusterInitCallback(EndpointId endpoint) +{ + ChipLogProgress(Zcl, "TV Casting Linux App: ContentAppObserverManager::SetDefaultDelegate"); + ContentAppObserver::SetDefaultDelegate(endpoint, &contentAppObserverManager); +} diff --git a/src/app/app-platform/ContentAppPlatform.cpp b/src/app/app-platform/ContentAppPlatform.cpp index 4162693b19508f..fa720418264739 100644 --- a/src/app/app-platform/ContentAppPlatform.cpp +++ b/src/app/app-platform/ContentAppPlatform.cpp @@ -451,7 +451,64 @@ void ContentAppPlatform::UnsetIfCurrentApp(ContentApp * app) } } -uint32_t ContentAppPlatform::GetPincodeFromContentApp(uint16_t vendorId, uint16_t productId, CharSpan rotatingId) +bool ContentAppPlatform::HasTargetContentApp(uint16_t vendorId, uint16_t productId, CharSpan rotatingId, + chip::Protocols::UserDirectedCommissioning::TargetAppInfo & info, uint32_t & passcode) +{ + // TODO: perform more complex search for matching apps + ContentApp * app = LoadContentAppByClient(info.vendorId, info.productId); + if (app == nullptr) + { + ChipLogProgress(DeviceLayer, "no app found for vendor id=%d \r\n", info.vendorId); + return false; + } + + if (app->GetApplicationBasicDelegate() == nullptr) + { + ChipLogProgress(DeviceLayer, "no ApplicationBasic cluster for app with vendor id=%d \r\n", info.vendorId); + return false; + } + + // first check if the vendor id matches the client + bool allow = app->GetApplicationBasicDelegate()->HandleGetVendorId() == vendorId; + if (!allow) + { + // if no match, then check allowed vendor list + for (const auto & allowedVendor : app->GetApplicationBasicDelegate()->GetAllowedVendorList()) + { + if (allowedVendor == vendorId) + { + allow = true; + break; + } + } + if (!allow) + { + ChipLogProgress( + DeviceLayer, + "no permission given by ApplicationBasic cluster on app with vendor id=%d to client with vendor id=%d\r\n", + info.vendorId, vendorId); + return false; + } + } + + if (app->GetAccountLoginDelegate() == nullptr) + { + ChipLogProgress(DeviceLayer, "no AccountLogin cluster for app with vendor id=%d \r\n", info.vendorId); + return true; + } + + static const size_t kSetupPasscodeSize = 12; + char mSetupPasscode[kSetupPasscodeSize]; + + app->GetAccountLoginDelegate()->GetSetupPin(mSetupPasscode, kSetupPasscodeSize, rotatingId); + std::string passcodeString(mSetupPasscode); + + char * eptr; + passcode = (uint32_t) strtol(passcodeString.c_str(), &eptr, 10); + return true; +} + +uint32_t ContentAppPlatform::GetPasscodeFromContentApp(uint16_t vendorId, uint16_t productId, CharSpan rotatingId) { ContentApp * app = LoadContentAppByClient(vendorId, productId); if (app == nullptr) @@ -466,14 +523,14 @@ uint32_t ContentAppPlatform::GetPincodeFromContentApp(uint16_t vendorId, uint16_ return 0; } - static const size_t kSetupPINSize = 12; - char mSetupPIN[kSetupPINSize]; + static const size_t kSetupPasscodeSize = 12; + char mSetupPasscode[kSetupPasscodeSize]; - app->GetAccountLoginDelegate()->GetSetupPin(mSetupPIN, kSetupPINSize, rotatingId); - std::string pinString(mSetupPIN); + app->GetAccountLoginDelegate()->GetSetupPin(mSetupPasscode, kSetupPasscodeSize, rotatingId); + std::string passcodeString(mSetupPasscode); char * eptr; - return (uint32_t) strtol(pinString.c_str(), &eptr, 10); + return (uint32_t) strtol(passcodeString.c_str(), &eptr, 10); } // Returns ACL entry with match subject or CHIP_ERROR_NOT_FOUND if no match is found diff --git a/src/app/app-platform/ContentAppPlatform.h b/src/app/app-platform/ContentAppPlatform.h index f05a94ceda7613..3dd0b138d2e9d1 100644 --- a/src/app/app-platform/ContentAppPlatform.h +++ b/src/app/app-platform/ContentAppPlatform.h @@ -27,6 +27,7 @@ #include #include #include +#include using chip::app::Clusters::ApplicationBasic::CatalogVendorApp; using chip::Controller::CommandResponseFailureCallback; @@ -148,9 +149,15 @@ class DLL_EXPORT ContentAppPlatform // unset this as current app, if it is current app void UnsetIfCurrentApp(ContentApp * app); - // loads content app identified by vid/pid of client and calls HandleGetSetupPin. - // Returns 0 if pin cannot be obtained. - uint32_t GetPincodeFromContentApp(uint16_t vendorId, uint16_t productId, CharSpan rotatingId); + // loads content app identified by vid/pid of client and calls HandleGetSetupPasscode. + // Returns 0 if passcode cannot be obtained. + uint32_t GetPasscodeFromContentApp(uint16_t vendorId, uint16_t productId, CharSpan rotatingId); + + // locates app identified by target info and confirms that it grants access to given vid/pid of client, + // loads given app and calls HandleGetSetupPasscode. Sets passcode to 0 if it cannot be obtained. + // return true if a matching app was found (and it granted this client access), even if a passcode was not obtained + bool HasTargetContentApp(uint16_t vendorId, uint16_t productId, CharSpan rotatingId, + chip::Protocols::UserDirectedCommissioning::TargetAppInfo & info, uint32_t & passcode); /** * @brief diff --git a/src/app/server/Dnssd.cpp b/src/app/server/Dnssd.cpp index 81c3754b891412..e91ef563007cfb 100644 --- a/src/app/server/Dnssd.cpp +++ b/src/app/server/Dnssd.cpp @@ -329,13 +329,20 @@ CHIP_ERROR DnssdServer::Advertise(bool commissionableNode, chip::Dnssd::Commissi } } } +#if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_PASSCODE + else + { + advertiseParameters.SetCommissionerPasscodeSupported(Optional(true)); + } +#endif auto & mdnsAdvertiser = chip::Dnssd::ServiceAdvertiser::Instance(); - ChipLogProgress(Discovery, "Advertise commission parameter vendorID=%u productID=%u discriminator=%04u/%02u cm=%u", + ChipLogProgress(Discovery, "Advertise commission parameter vendorID=%u productID=%u discriminator=%04u/%02u cm=%u cp=%u", advertiseParameters.GetVendorId().ValueOr(0), advertiseParameters.GetProductId().ValueOr(0), advertiseParameters.GetLongDiscriminator(), advertiseParameters.GetShortDiscriminator(), - to_underlying(advertiseParameters.GetCommissioningMode())); + to_underlying(advertiseParameters.GetCommissioningMode()), + advertiseParameters.GetCommissionerPasscodeSupported().ValueOr(false) ? 1 : 0); return mdnsAdvertiser.Advertise(advertiseParameters); } diff --git a/src/app/server/Server.cpp b/src/app/server/Server.cpp index eb9b5835dd23d5..4a0fa91f56d520 100644 --- a/src/app/server/Server.cpp +++ b/src/app/server/Server.cpp @@ -567,65 +567,84 @@ void Server::Shutdown() // NOTE: UDC client is located in Server.cpp because it really only makes sense // to send UDC from a Matter device. The UDC message payload needs to include the device's // randomly generated service name. -CHIP_ERROR Server::SendUserDirectedCommissioningRequest(chip::Transport::PeerAddress commissioner) +CHIP_ERROR Server::SendUserDirectedCommissioningRequest(chip::Transport::PeerAddress commissioner, + Protocols::UserDirectedCommissioning::IdentificationDeclaration & id) { ChipLogDetail(AppServer, "SendUserDirectedCommissioningRequest2"); CHIP_ERROR err; - char nameBuffer[chip::Dnssd::Commission::kInstanceNameMaxLength + 1]; - err = app::DnssdServer::Instance().GetCommissionableInstanceName(nameBuffer, sizeof(nameBuffer)); - if (err != CHIP_NO_ERROR) - { - ChipLogError(AppServer, "Failed to get mdns instance name error: %" CHIP_ERROR_FORMAT, err.Format()); - return err; - } - ChipLogDetail(AppServer, "instanceName=%s", nameBuffer); - - Protocols::UserDirectedCommissioning::IdentificationDeclaration id; - id.SetInstanceName(nameBuffer); - uint16_t vendorId = 0; - if (DeviceLayer::GetDeviceInstanceInfoProvider()->GetVendorId(vendorId) != CHIP_NO_ERROR) + // only populate fields left blank by the client + if (strlen(id.GetInstanceName()) == 0) { - ChipLogDetail(Discovery, "Vendor ID not known"); - } - else - { - id.SetVendorId(vendorId); + char nameBuffer[chip::Dnssd::Commission::kInstanceNameMaxLength + 1]; + err = app::DnssdServer::Instance().GetCommissionableInstanceName(nameBuffer, sizeof(nameBuffer)); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Failed to get mdns instance name error: %" CHIP_ERROR_FORMAT, err.Format()); + return err; + } + id.SetInstanceName(nameBuffer); } + ChipLogDetail(AppServer, "instanceName=%s", id.GetInstanceName()); - uint16_t productId = 0; - if (DeviceLayer::GetDeviceInstanceInfoProvider()->GetProductId(productId) != CHIP_NO_ERROR) - { - ChipLogDetail(Discovery, "Product ID not known"); - } - else + if (id.GetVendorId() == 0) { - id.SetProductId(productId); + uint16_t vendorId = 0; + if (DeviceLayer::GetDeviceInstanceInfoProvider()->GetVendorId(vendorId) != CHIP_NO_ERROR) + { + ChipLogDetail(Discovery, "Vendor ID not known"); + } + else + { + id.SetVendorId(vendorId); + } } - char deviceName[chip::Dnssd::kKeyDeviceNameMaxLength + 1] = {}; - if (!chip::DeviceLayer::ConfigurationMgr().IsCommissionableDeviceNameEnabled() || - chip::DeviceLayer::ConfigurationMgr().GetCommissionableDeviceName(deviceName, sizeof(deviceName)) != CHIP_NO_ERROR) + if (id.GetProductId() == 0) { - ChipLogDetail(Discovery, "Device Name not known"); + uint16_t productId = 0; + if (DeviceLayer::GetDeviceInstanceInfoProvider()->GetProductId(productId) != CHIP_NO_ERROR) + { + ChipLogDetail(Discovery, "Product ID not known"); + } + else + { + id.SetProductId(productId); + } } - else + + if (strlen(id.GetDeviceName()) == 0) { - id.SetDeviceName(deviceName); + char deviceName[chip::Dnssd::kKeyDeviceNameMaxLength + 1] = {}; + if (!chip::DeviceLayer::ConfigurationMgr().IsCommissionableDeviceNameEnabled() || + chip::DeviceLayer::ConfigurationMgr().GetCommissionableDeviceName(deviceName, sizeof(deviceName)) != CHIP_NO_ERROR) + { + ChipLogDetail(Discovery, "Device Name not known"); + } + else + { + id.SetDeviceName(deviceName); + } } #if CHIP_ENABLE_ROTATING_DEVICE_ID && defined(CHIP_DEVICE_CONFIG_ROTATING_DEVICE_ID_UNIQUE_ID) - char rotatingDeviceIdHexBuffer[RotatingDeviceId::kHexMaxLength]; - ReturnErrorOnFailure( - app::DnssdServer::Instance().GenerateRotatingDeviceId(rotatingDeviceIdHexBuffer, ArraySize(rotatingDeviceIdHexBuffer))); + if (id.GetRotatingIdLength() == 0) + { + char rotatingDeviceIdHexBuffer[RotatingDeviceId::kHexMaxLength]; + ReturnErrorOnFailure( + app::DnssdServer::Instance().GenerateRotatingDeviceId(rotatingDeviceIdHexBuffer, ArraySize(rotatingDeviceIdHexBuffer))); - uint8_t * rotatingId = reinterpret_cast(rotatingDeviceIdHexBuffer); - size_t rotatingIdLen = strlen(rotatingDeviceIdHexBuffer); - id.SetRotatingId(rotatingId, rotatingIdLen); + uint8_t * rotatingId = reinterpret_cast(rotatingDeviceIdHexBuffer); + size_t rotatingIdLen = strlen(rotatingDeviceIdHexBuffer); + id.SetRotatingId(rotatingId, rotatingIdLen); + } #endif - id.SetCdPort(mCdcListenPort); + if (id.GetCdPort() == 0) + { + id.SetCdPort(mCdcListenPort); + } err = gUDCClient->SendUDCMessage(&mTransports, id, commissioner); diff --git a/src/app/server/Server.h b/src/app/server/Server.h index e6263ccddc6431..e574b52b159957 100644 --- a/src/app/server/Server.h +++ b/src/app/server/Server.h @@ -314,7 +314,14 @@ class Server CHIP_ERROR Init(const ServerInitParams & initParams); #if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT - CHIP_ERROR SendUserDirectedCommissioningRequest(chip::Transport::PeerAddress commissioner); + CHIP_ERROR + SendUserDirectedCommissioningRequest(chip::Transport::PeerAddress commissioner, + Protocols::UserDirectedCommissioning::IdentificationDeclaration & id); + + Protocols::UserDirectedCommissioning::UserDirectedCommissioningClient * GetUserDirectedCommissioningClient() + { + return gUDCClient; + } #endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT /** diff --git a/src/controller/CommissionerDiscoveryController.cpp b/src/controller/CommissionerDiscoveryController.cpp index bd6bee4d098078..c1c6492636ecd0 100644 --- a/src/controller/CommissionerDiscoveryController.cpp +++ b/src/controller/CommissionerDiscoveryController.cpp @@ -50,6 +50,47 @@ void CommissionerDiscoveryController::OnUserDirectedCommissioningRequest(UDCClie ChipLogDetail(Controller, "CommissionerDiscoveryController not read. Current instance=%s", mCurrentInstance); return; } + // first check if this is a cancel + if (state.GetCancelPasscode()) + { + ChipLogDetail(Controller, "------PROMPT USER: %s cancelled commissioning [" ChipLogFormatMEI "," ChipLogFormatMEI ",%s]", + state.GetDeviceName(), ChipLogValueMEI(state.GetVendorId()), ChipLogValueMEI(state.GetProductId()), + state.GetInstanceName()); + if (mUserPrompter != nullptr) + { + mUserPrompter->HidePromptsOnCancel(state.GetVendorId(), state.GetProductId(), state.GetDeviceName()); + } + return; + } + if (state.GetCommissionerPasscodeReady() && state.GetCdPort() != 0) + { + uint32_t passcode = state.GetCachedCommissionerPasscode(); + if (!mReady || passcode == 0) + { + ChipLogError(AppServer, "On UDC: commissioner passcode ready but no passcode"); + CommissionerDeclaration cd; + cd.SetErrorCode(CommissionerDeclaration::CdError::kUnexpectedCommissionerPasscodeReady); + + if (mUdcServer == nullptr) + { + ChipLogError(AppServer, "On UDC: no udc server"); + return; + } + mUdcServer->SendCDCMessage(cd, + chip::Transport::PeerAddress::UDP(state.GetPeerAddress().GetIPAddress(), state.GetCdPort())); + return; + } + else + { + // can only get here is ok() has already been called + ChipLogError(AppServer, "On UDC: commissioner passcode ready with passcode - commissioning"); + + // start commissioning using the cached passcode + CommissionWithPasscode(passcode); + return; + } + } + mReady = false; Platform::CopyString(mCurrentInstance, state.GetInstanceName()); mPendingConsent = true; @@ -73,7 +114,7 @@ void CommissionerDiscoveryController::Ok() { if (!mPendingConsent) { - ChipLogError(AppServer, "UX Cancel: no current instance"); + ChipLogError(AppServer, "UX Ok: no current instance"); return; } if (mUdcServer == nullptr) @@ -94,7 +135,7 @@ void CommissionerDiscoveryController::Ok() } client->SetUDCClientProcessingState(UDCClientProcessingState::kObtainingOnboardingPayload); - if (mPincodeService != nullptr) + if (mPasscodeService != nullptr) { char rotatingIdString[chip::Dnssd::kMaxRotatingIdLen * 2 + 1] = ""; Encoding::BytesToUppercaseHexString(client->GetRotatingId(), client->GetRotatingIdLength(), rotatingIdString, @@ -103,24 +144,122 @@ void CommissionerDiscoveryController::Ok() // sizeof(rotatingIdString)); CharSpan rotatingIdSpan = chip::CharSpan(rotatingIdString, sizeof(rotatingIdString)); - uint32_t pincode = - mPincodeService->FetchCommissionPincodeFromContentApp(client->GetVendorId(), client->GetProductId(), rotatingIdSpan); - if (pincode != 0) + uint32_t passcode = 0; + uint8_t targetAppCount = client->GetNumTargetAppInfos(); + if (targetAppCount > 0) + { + bool hasTargetApp = false; + for (uint8_t i = 0; i < targetAppCount; i++) + { + TargetAppInfo info; + if (client->GetTargetAppInfo(i, info)) + { + if (mPasscodeService->HasTargetContentApp(client->GetVendorId(), client->GetProductId(), rotatingIdSpan, info, + passcode)) + { + // found one + hasTargetApp = true; + } + } + } + // handle NoAppsFound CDC case + if (!hasTargetApp) + { + ChipLogError(AppServer, "UX Ok: target apps specified but none found, sending CDC"); + CommissionerDeclaration cd; + cd.SetNoAppsFound(true); + mUdcServer->SendCDCMessage( + cd, chip::Transport::PeerAddress::UDP(client->GetPeerAddress().GetIPAddress(), client->GetCdPort())); + return; + } + } + else + { + passcode = mPasscodeService->FetchCommissionPasscodeFromContentApp(client->GetVendorId(), client->GetProductId(), + rotatingIdSpan); + } + + // if CommissionerPasscode + // - if CommissionerPasscodeReady, then start commissioning + // - if CommissionerPasscode, then call new UX method to show passcode, send CDC + if (passcode == 0 && client->GetCommissionerPasscode() && client->GetCdPort() != 0) + { + // first step of commissioner passcode + ChipLogError(AppServer, "UX Ok: commissioner passcode, sending CDC"); + // generate a passcode + passcode = mPasscodeService->GetCommissionerPasscode(client->GetVendorId(), client->GetProductId(), rotatingIdSpan); + if (passcode == 0) + { + // passcode feature disabled + ChipLogError(AppServer, "UX Ok: commissioner passcode disabled, sending CDC with error"); + CommissionerDeclaration cd; + cd.SetErrorCode(CommissionerDeclaration::CdError::kCommissionerPasscodeDisabled); + cd.SetNeedsPasscode(true); + mUdcServer->SendCDCMessage( + cd, chip::Transport::PeerAddress::UDP(client->GetPeerAddress().GetIPAddress(), client->GetCdPort())); + return; + } + client->SetCachedCommissionerPasscode(passcode); + + CommissionerDeclaration cd; + cd.SetCommissionerPasscode(true); + if (mUserPrompter->DisplaysPasscodeAndQRCode()) + { + cd.SetQRCodeDisplayed(true); + } + mUdcServer->SendCDCMessage( + cd, chip::Transport::PeerAddress::UDP(client->GetPeerAddress().GetIPAddress(), client->GetCdPort())); + + // dialog + ChipLogDetail(Controller, + "------PROMPT USER: %s is requesting permission to cast to this TV. Casting passcode: [" ChipLogFormatMEI + "]. Additional instructions [" ChipLogFormatMEI "] [%s]. [" ChipLogFormatMEI "," ChipLogFormatMEI ",%s]", + client->GetDeviceName(), ChipLogValueMEI(passcode), ChipLogValueMEI(client->GetPairingHint()), + client->GetPairingInst(), ChipLogValueMEI(client->GetVendorId()), ChipLogValueMEI(client->GetProductId()), + client->GetInstanceName()); + mUserPrompter->PromptWithCommissionerPasscode(client->GetVendorId(), client->GetProductId(), client->GetDeviceName(), + passcode, client->GetPairingHint(), client->GetPairingInst()); + return; + } + if (passcode != 0) { - CommissionWithPincode(pincode); + CommissionWithPasscode(passcode); return; } } - ChipLogDetail(Controller, "------PROMPT USER: please enter pin displayed in casting app "); + // if NoPasscode, send CDC + if (client->GetNoPasscode() && client->GetCdPort() != 0) + { + ChipLogError(AppServer, "UX Ok: no app passcode and NoPasscode in UDC, sending CDC"); + CommissionerDeclaration cd; + cd.SetNeedsPasscode(true); + mUdcServer->SendCDCMessage(cd, + chip::Transport::PeerAddress::UDP(client->GetPeerAddress().GetIPAddress(), client->GetCdPort())); + return; + } + + // if CdUponPasscodeDialog, send CDC + if (client->GetCdUponPasscodeDialog() && client->GetCdPort() != 0) + { + ChipLogError(AppServer, "UX Ok: no app passcode and GetCdUponPasscodeDialog in UDC, sending CDC"); + CommissionerDeclaration cd; + cd.SetNeedsPasscode(true); // TODO: should this be set? + cd.SetPasscodeDialogDisplayed(true); + mUdcServer->SendCDCMessage(cd, + chip::Transport::PeerAddress::UDP(client->GetPeerAddress().GetIPAddress(), client->GetCdPort())); + } + + ChipLogDetail(Controller, "------PROMPT USER: please enter passcode displayed in casting app "); if (mUserPrompter != nullptr) { - mUserPrompter->PromptForCommissionPincode(client->GetVendorId(), client->GetProductId(), client->GetDeviceName()); + mUserPrompter->PromptForCommissionPasscode(client->GetVendorId(), client->GetProductId(), client->GetDeviceName(), + client->GetPairingHint(), client->GetPairingInst()); } - ChipLogDetail(Controller, "------Via Shell Enter: controller ux ok [pincode]"); + ChipLogDetail(Controller, "------Via Shell Enter: controller ux ok [passcode]"); } -void CommissionerDiscoveryController::CommissionWithPincode(uint32_t pincode) +void CommissionerDiscoveryController::CommissionWithPasscode(uint32_t passcode) { if (!mPendingConsent) { @@ -129,7 +268,7 @@ void CommissionerDiscoveryController::CommissionWithPincode(uint32_t pincode) } if (mUdcServer == nullptr) { - ChipLogError(AppServer, "UX CommissionWithPincode: no udc server"); + ChipLogError(AppServer, "UX CommissionWithPasscode: no udc server"); return; } UDCClientState * client = mUdcServer->GetUDCClients().FindUDCClientState(mCurrentInstance); @@ -142,14 +281,18 @@ void CommissionerDiscoveryController::CommissionWithPincode(uint32_t pincode) if (!(client->GetUDCClientProcessingState() == UDCClientProcessingState::kPromptingUser || client->GetUDCClientProcessingState() == UDCClientProcessingState::kObtainingOnboardingPayload)) { - ChipLogError(AppServer, "UX CommissionWithPincode: invalid state for CommissionWithPincode"); + ChipLogError(AppServer, "UX CommissionWithPasscode: invalid state for CommissionWithPasscode"); return; } Transport::PeerAddress peerAddress = client->GetPeerAddress(); client->SetUDCClientProcessingState(UDCClientProcessingState::kCommissioningNode); if (mCommissionerCallback != nullptr) { - mCommissionerCallback->ReadyForCommissioning(pincode, client->GetLongDiscriminator(), peerAddress); + if (mUserPrompter != nullptr) + { + mUserPrompter->PromptCommissioningStarted(client->GetVendorId(), client->GetProductId(), client->GetDeviceName()); + } + mCommissionerCallback->ReadyForCommissioning(passcode, client->GetLongDiscriminator(), peerAddress); } } diff --git a/src/controller/CommissionerDiscoveryController.h b/src/controller/CommissionerDiscoveryController.h index 8d7fee99e3d25b..42a0bb6f327fc9 100644 --- a/src/controller/CommissionerDiscoveryController.h +++ b/src/controller/CommissionerDiscoveryController.h @@ -65,18 +65,70 @@ class DLL_EXPORT UserPrompter /** * @brief - * Called to prompt the user to enter the setup pincode displayed by the given commissioneeName/vendorId/productId to be - * commissioned. For example "Please enter pin displayed in casting app." + * 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 with pin then implementor should call CommissionerRespondPincode(uint32_t pincode); + * 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 PromptForCommissionPincode(uint16_t vendorId, uint16_t productId, const char * commissioneeName) = 0; + 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 @@ -101,13 +153,41 @@ class DLL_EXPORT UserPrompter virtual ~UserPrompter() = default; }; -class DLL_EXPORT PincodeService +// TODO: rename this to Passcode? +class DLL_EXPORT PasscodeService { public: /** * @brief - * Called to get the setup pincode from the content app corresponding to the given vendorId/productId - * Returns 0 if pincode cannot be obtained + * Called to determine if the given target app is available to the commissionee with the given given + * vendorId/productId, and if so, return the passcode. + * + * @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. + * @param[in] passcode Passcode for the given commissionee, or 0 if passcode cannot be obtained. + * + */ + virtual bool HasTargetContentApp(uint16_t vendorId, uint16_t productId, chip::CharSpan rotatingId, + chip::Protocols::UserDirectedCommissioning::TargetAppInfo & info, uint32_t & passcode) = 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 + * Returns 0 if passcode cannot be obtained * * If user responds with OK then implementor should call CommissionerRespondOk(); * If user responds with Cancel then implementor should call CommissionerRespondCancel(); @@ -117,9 +197,9 @@ class DLL_EXPORT PincodeService * @param[in] rotatingId The rotatingId in the DNS-SD advertisement of the requesting commissionee. * */ - virtual uint32_t FetchCommissionPincodeFromContentApp(uint16_t vendorId, uint16_t productId, chip::CharSpan rotatingId) = 0; + virtual uint32_t FetchCommissionPasscodeFromContentApp(uint16_t vendorId, uint16_t productId, chip::CharSpan rotatingId) = 0; - virtual ~PincodeService() = default; + virtual ~PasscodeService() = default; }; class DLL_EXPORT PostCommissioningListener @@ -152,12 +232,12 @@ class DLL_EXPORT CommissionerCallback * Called to notify the commissioner that commissioning can now proceed for * the node identified by the given arguments. * - * @param[in] pincode The pin code to use for the commissionee. + * @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 pincode, uint16_t longDiscriminator, PeerAddress peerAddress) = 0; + virtual void ReadyForCommissioning(uint32_t passcode, uint16_t longDiscriminator, PeerAddress peerAddress) = 0; virtual ~CommissionerCallback() = default; }; @@ -201,10 +281,10 @@ class CommissionerDiscoveryController : public chip::Protocols::UserDirectedComm void Cancel(); /** - * This method should be called with the pincode for the client - * indicated in the UserPrompter's PromptForCommissionPincode callback + * This method should be called with the passcode for the client + * indicated in the UserPrompter's PromptForCommissionPasscode callback */ - void CommissionWithPincode(uint32_t pincode); + void CommissionWithPasscode(uint32_t passcode); /** * This method should be called by the commissioner to indicate that commissioning succeeded. @@ -253,9 +333,9 @@ class CommissionerDiscoveryController : public chip::Protocols::UserDirectedComm inline void SetUserPrompter(UserPrompter * userPrompter) { mUserPrompter = userPrompter; } /** - * Assign a PincodeService + * Assign a PasscodeService */ - inline void SetPincodeService(PincodeService * pincodeService) { mPincodeService = pincodeService; } + inline void SetPasscodeService(PasscodeService * passcodeService) { mPasscodeService = passcodeService; } /** * Assign a Commissioner Callback to perform commissioning once user consent has been given @@ -293,7 +373,7 @@ class CommissionerDiscoveryController : public chip::Protocols::UserDirectedComm UserDirectedCommissioningServer * mUdcServer = nullptr; UserPrompter * mUserPrompter = nullptr; - PincodeService * mPincodeService = nullptr; + PasscodeService * mPasscodeService = nullptr; CommissionerCallback * mCommissionerCallback = nullptr; PostCommissioningListener * mPostCommissioningListener = nullptr; }; diff --git a/src/include/platform/CHIPDeviceConfig.h b/src/include/platform/CHIPDeviceConfig.h index bafca37458c8a3..9734e4bfa28b35 100644 --- a/src/include/platform/CHIPDeviceConfig.h +++ b/src/include/platform/CHIPDeviceConfig.h @@ -844,6 +844,20 @@ #define CHIP_DEVICE_CONFIG_PAIRING_SECONDARY_INSTRUCTION "" #endif +/** + * CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_PASSCODE + * + * Enable or disable commissioner passcode feature. + * With this feature enabled, the commissioner can generate a commissioning passcode + * and display it to the user so that the user can enter it into a commissionable + * node, such as a phone app, during user directed commissioning. + * + * For Video Players, this value will often be 1 + */ +#ifndef CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_PASSCODE +#define CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_PASSCODE 0 +#endif + // -------------------- Thread Configuration -------------------- /** diff --git a/src/lib/dnssd/Advertiser.h b/src/lib/dnssd/Advertiser.h index bfb587acec2ad7..0a98a90fa89a7e 100644 --- a/src/lib/dnssd/Advertiser.h +++ b/src/lib/dnssd/Advertiser.h @@ -263,6 +263,13 @@ class CommissionAdvertisingParameters : public BaseAdvertisingParams commissionerPasscodeSupported) + { + mCommissionerPasscodeSupported = commissionerPasscodeSupported; + return *this; + } + Optional GetCommissionerPasscodeSupported() const { return mCommissionerPasscodeSupported; } + private: uint8_t mShortDiscriminator = 0; uint16_t mLongDiscriminator = 0; // 12-bit according to spec @@ -281,6 +288,8 @@ class CommissionAdvertisingParameters : public BaseAdvertisingParams mCommissionerPasscodeSupported; }; /** diff --git a/src/lib/dnssd/Advertiser_ImplMinimalMdns.cpp b/src/lib/dnssd/Advertiser_ImplMinimalMdns.cpp index 92499460b0db5c..a77abebab68a58 100644 --- a/src/lib/dnssd/Advertiser_ImplMinimalMdns.cpp +++ b/src/lib/dnssd/Advertiser_ImplMinimalMdns.cpp @@ -849,6 +849,10 @@ FullQName AdvertiserMinMdns::GetCommissioningTxtEntries(const CommissionAdvertis char txtRotatingDeviceId[chip::Dnssd::kKeyRotatingDeviceIdMaxLength + 4]; char txtPairingHint[chip::Dnssd::kKeyPairingInstructionMaxLength + 4]; char txtPairingInstr[chip::Dnssd::kKeyPairingInstructionMaxLength + 4]; + + // the following sub types only apply to commissioner discovery advertisements + char txtCommissionerPasscode[chip::Dnssd::kKeyCommissionerPasscodeMaxLength + 4]; + if (params.GetCommissionAdvertiseMode() == CommssionAdvertiseMode::kCommissionableNode) { // a discriminator always exists @@ -876,6 +880,14 @@ FullQName AdvertiserMinMdns::GetCommissioningTxtEntries(const CommissionAdvertis txtFields[numTxtFields++] = txtPairingInstr; } } + else + { + if (params.GetCommissionerPasscodeSupported().ValueOr(false)) + { + snprintf(txtCommissionerPasscode, sizeof(txtCommissionerPasscode), "CP=%d", static_cast(1)); + txtFields[numTxtFields++] = txtCommissionerPasscode; + } + } if (numTxtFields == 0) { return allocator->AllocateQNameFromArray(mEmptyTextEntries, 1); diff --git a/src/lib/dnssd/Discovery_ImplPlatform.cpp b/src/lib/dnssd/Discovery_ImplPlatform.cpp index 55debe8eb85e8b..240bb979991038 100644 --- a/src/lib/dnssd/Discovery_ImplPlatform.cpp +++ b/src/lib/dnssd/Discovery_ImplPlatform.cpp @@ -251,6 +251,9 @@ CHIP_ERROR CopyTxtRecord(TxtFieldKey key, char * buffer, size_t bufferLen, const return CopyTextRecordValue(buffer, bufferLen, params.GetPairingHint()); case TxtFieldKey::kCommissioningMode: return CopyTextRecordValue(buffer, bufferLen, params.GetCommissioningMode()); + case TxtFieldKey::kCommissionerPasscode: + return CopyTextRecordValue(buffer, bufferLen, + static_cast(params.GetCommissionerPasscodeSupported().ValueOr(false) ? 1 : 0)); default: return CopyTxtRecord(key, buffer, bufferLen, static_cast>(params)); } @@ -580,6 +583,7 @@ CHIP_ERROR DiscoveryImplPlatform::Advertise(const CommissionAdvertisingParameter if (params.GetCommissionAdvertiseMode() == CommssionAdvertiseMode::kCommissioner) { + ADD_TXT_RECORD(CommissionerPasscode); PUBLISH_RECORDS(Commissioner); return CHIP_NO_ERROR; } diff --git a/src/lib/dnssd/Resolver.h b/src/lib/dnssd/Resolver.h index ae4e95f4341b85..03e68e22df0c4a 100644 --- a/src/lib/dnssd/Resolver.h +++ b/src/lib/dnssd/Resolver.h @@ -177,6 +177,7 @@ struct CommissionNodeData size_t rotatingIdLen = 0; uint16_t pairingHint = 0; char pairingInstruction[kMaxPairingInstructionLen + 1] = {}; + uint8_t commissionerPasscode = 0; CommissionNodeData() {} @@ -230,6 +231,10 @@ struct CommissionNodeData ChipLogDetail(Discovery, "\tInstance Name: %s", instanceName); } ChipLogDetail(Discovery, "\tCommissioning Mode: %u", commissioningMode); + if (commissionerPasscode > 0) + { + ChipLogDetail(Discovery, "\tCommissioner Passcode: %u", commissionerPasscode); + } } }; diff --git a/src/lib/dnssd/TxtFields.cpp b/src/lib/dnssd/TxtFields.cpp index db11983ea869d7..d43650748e53a2 100644 --- a/src/lib/dnssd/TxtFields.cpp +++ b/src/lib/dnssd/TxtFields.cpp @@ -183,6 +183,11 @@ void GetPairingInstruction(const ByteSpan & value, char * pairingInstruction) Platform::CopyString(pairingInstruction, kMaxPairingInstructionLen + 1, value); } +uint8_t GetCommissionerPasscode(const ByteSpan & value) +{ + return MakeU8FromAsciiDecimal(value); +} + Optional GetRetryInterval(const ByteSpan & value) { const auto undefined = std::numeric_limits::max(); @@ -250,6 +255,9 @@ void FillNodeDataFromTxt(const ByteSpan & key, const ByteSpan & val, CommissionN case TxtFieldKey::kPairingHint: nodeData.pairingHint = Internal::GetPairingHint(val); break; + case TxtFieldKey::kCommissionerPasscode: + nodeData.commissionerPasscode = Internal::GetCommissionerPasscode(val); + break; default: break; } diff --git a/src/lib/dnssd/TxtFields.h b/src/lib/dnssd/TxtFields.h index 6e84fa372a8339..c3b8a0d668682c 100644 --- a/src/lib/dnssd/TxtFields.h +++ b/src/lib/dnssd/TxtFields.h @@ -39,14 +39,15 @@ static constexpr size_t kKeyTcpSupportedMaxLength = 1; static constexpr size_t kKeyLongIdleTimeICDMaxLength = 1; // Commissionable/commissioner node TXT entries -static constexpr size_t kKeyLongDiscriminatorMaxLength = 5; -static constexpr size_t kKeyVendorProductMaxLength = 11; -static constexpr size_t kKeyCommissioningModeMaxLength = 1; -static constexpr size_t kKeyDeviceTypeMaxLength = 10; -static constexpr size_t kKeyDeviceNameMaxLength = 32; -static constexpr size_t kKeyRotatingDeviceIdMaxLength = 100; -static constexpr size_t kKeyPairingInstructionMaxLength = 128; -static constexpr size_t kKeyPairingHintMaxLength = 10; +static constexpr size_t kKeyLongDiscriminatorMaxLength = 5; +static constexpr size_t kKeyVendorProductMaxLength = 11; +static constexpr size_t kKeyCommissioningModeMaxLength = 1; +static constexpr size_t kKeyDeviceTypeMaxLength = 10; +static constexpr size_t kKeyDeviceNameMaxLength = 32; +static constexpr size_t kKeyRotatingDeviceIdMaxLength = 100; +static constexpr size_t kKeyPairingInstructionMaxLength = 128; +static constexpr size_t kKeyPairingHintMaxLength = 10; +static constexpr size_t kKeyCommissionerPasscodeMaxLength = 1; enum class TxtKeyUse : uint8_t { @@ -66,6 +67,7 @@ enum class TxtFieldKey : uint8_t kRotatingDeviceId, kPairingInstruction, kPairingHint, + kCommissionerPasscode, kSessionIdleInterval, kSessionActiveInterval, kSessionActiveThreshold, @@ -93,6 +95,7 @@ constexpr const TxtFieldInfo txtFieldInfo[static_cast(TxtFieldKey::kCoun { TxtFieldKey::kRotatingDeviceId, kKeyRotatingDeviceIdMaxLength, "RI", TxtKeyUse::kCommission }, { TxtFieldKey::kPairingInstruction, kKeyPairingInstructionMaxLength, "PI", TxtKeyUse::kCommission }, { TxtFieldKey::kPairingHint, kKeyPairingHintMaxLength, "PH", TxtKeyUse::kCommission }, + { TxtFieldKey::kCommissionerPasscode, kKeyCommissionerPasscodeMaxLength, "CP", TxtKeyUse::kCommission }, { TxtFieldKey::kSessionIdleInterval, kKeySessionIdleIntervalMaxLength, "SII", TxtKeyUse::kCommon }, { TxtFieldKey::kSessionActiveInterval, kKeySessionActiveIntervalMaxLength, "SAI", TxtKeyUse::kCommon }, { TxtFieldKey::kSessionActiveThreshold, kKeySessionActiveThresholdMaxLength, "SAT", TxtKeyUse::kCommon }, @@ -112,6 +115,7 @@ void GetDeviceName(const ByteSpan & value, char * name); void GetRotatingDeviceId(const ByteSpan & value, uint8_t * rotatingId, size_t * len); uint16_t GetPairingHint(const ByteSpan & value); void GetPairingInstruction(const ByteSpan & value, char * pairingInstruction); +uint8_t GetCommissionerPasscode(const ByteSpan & value); #endif } // namespace Internal diff --git a/src/lib/dnssd/tests/TestTxtFields.cpp b/src/lib/dnssd/tests/TestTxtFields.cpp index 9c3f6bd08ae5ba..95f13b5d60c091 100644 --- a/src/lib/dnssd/tests/TestTxtFields.cpp +++ b/src/lib/dnssd/tests/TestTxtFields.cpp @@ -85,6 +85,9 @@ void TestGetTxtFieldKey(nlTestSuite * inSuite, void * inContext) strcpy(key, "XX"); NL_TEST_ASSERT(inSuite, GetTxtFieldKey(GetSpan(key)) == TxtFieldKey::kUnknown); + + strcpy(key, "CP"); + NL_TEST_ASSERT(inSuite, GetTxtFieldKey(GetSpan(key)) == TxtFieldKey::kCommissionerPasscode); } void TestGetTxtFieldKeyCaseInsensitive(nlTestSuite * inSuite, void * inContext) @@ -284,6 +287,20 @@ void TestGetPairingInstruction(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, strcmp(ret, data) == 0); } +void TestGetCommissionerPasscode(nlTestSuite * inSuite, void * inContext) +{ + char cm[64]; + strcpy(cm, "0"); + NL_TEST_ASSERT(inSuite, GetCommissionerPasscode(GetSpan(cm)) == 0); + + strcpy(cm, "1"); + NL_TEST_ASSERT(inSuite, GetCommissionerPasscode(GetSpan(cm)) == 1); + + // overflow a uint8 + sprintf(cm, "%u", static_cast(std::numeric_limits::max()) + 1); + NL_TEST_ASSERT(inSuite, GetCommissionerPasscode(GetSpan(cm)) == 0); +} + bool NodeDataIsEmpty(const DiscoveredNodeData & node) { @@ -292,7 +309,7 @@ bool NodeDataIsEmpty(const DiscoveredNodeData & node) node.commissionData.rotatingIdLen != 0 || node.commissionData.pairingHint != 0 || node.resolutionData.mrpRetryIntervalIdle.HasValue() || node.resolutionData.mrpRetryIntervalActive.HasValue() || node.resolutionData.mrpRetryActiveThreshold.HasValue() || node.resolutionData.isICDOperatingAsLIT.HasValue() || - node.resolutionData.supportsTcp) + node.resolutionData.supportsTcp || node.commissionData.commissionerPasscode != 0) { return false; } @@ -343,6 +360,14 @@ void TestFillDiscoveredNodeDataFromTxt(nlTestSuite * inSuite, void * inContext) filled.commissionData.commissioningMode = 0; NL_TEST_ASSERT(inSuite, NodeDataIsEmpty(filled)); + // Commissioning mode + strcpy(key, "CP"); + strcpy(val, "1"); + FillNodeDataFromTxt(GetSpan(key), GetSpan(val), filled.commissionData); + NL_TEST_ASSERT(inSuite, filled.commissionData.commissionerPasscode == 1); + filled.commissionData.commissionerPasscode = 0; + NL_TEST_ASSERT(inSuite, NodeDataIsEmpty(filled)); + // Device type strcpy(key, "DT"); strcpy(val, "1"); @@ -744,6 +769,7 @@ const nlTest sTests[] = { NL_TEST_DEF("TxtFieldRotatingDeviceId", TestGetRotatingDeviceId), // NL_TEST_DEF("TxtFieldPairingHint", TestGetPairingHint), // NL_TEST_DEF("TxtFieldPairingInstruction", TestGetPairingInstruction), // + NL_TEST_DEF("TxtFieldCommissionerPasscode", TestGetCommissionerPasscode), // NL_TEST_DEF("TxtFieldFillDiscoveredNodeDataFromTxt", TestFillDiscoveredNodeDataFromTxt), // NL_TEST_DEF("TxtDiscoveredFieldMrpRetryIntervalIdle", TxtFieldSessionIdleInterval), NL_TEST_DEF("TxtDiscoveredFieldMrpRetryIntervalActive", TxtFieldSessionActiveInterval), diff --git a/src/protocols/user_directed_commissioning/UDCClientState.h b/src/protocols/user_directed_commissioning/UDCClientState.h index 944a1e156ad9f2..587b1e3ff2b64c 100644 --- a/src/protocols/user_directed_commissioning/UDCClientState.h +++ b/src/protocols/user_directed_commissioning/UDCClientState.h @@ -118,7 +118,7 @@ class UDCClientState } return false; } - size_t GetNumTargetAppInfos() const { return mNumTargetAppInfos; } + uint8_t GetNumTargetAppInfos() const { return mNumTargetAppInfos; } bool AddTargetAppInfo(TargetAppInfo vid) { @@ -160,27 +160,31 @@ class UDCClientState void SetCancelPasscode(bool newValue) { mCancelPasscode = newValue; }; bool GetCancelPasscode() const { return mCancelPasscode; }; + void SetCachedCommissionerPasscode(uint32_t newValue) { mCachedCommissionerPasscode = newValue; }; + uint32_t GetCachedCommissionerPasscode() const { return mCachedCommissionerPasscode; }; + /** * Reset the connection state to a completely uninitialized status. */ void Reset() { - mPeerAddress = PeerAddress::Uninitialized(); - mLongDiscriminator = 0; - mVendorId = 0; - mProductId = 0; - mRotatingIdLen = 0; - mCdPort = 0; - mDeviceName[0] = '\0'; - mPairingInst[0] = '\0'; - mPairingHint = 0; - mNoPasscode = false; - mCdUponPasscodeDialog = false; - mCommissionerPasscode = false; - mCommissionerPasscodeReady = false; - mCancelPasscode = false; - mExpirationTime = System::Clock::kZero; - mUDCClientProcessingState = UDCClientProcessingState::kNotInitialized; + mPeerAddress = PeerAddress::Uninitialized(); + mLongDiscriminator = 0; + mVendorId = 0; + mProductId = 0; + mRotatingIdLen = 0; + mCdPort = 0; + mDeviceName[0] = '\0'; + mPairingInst[0] = '\0'; + mPairingHint = 0; + mNoPasscode = false; + mCdUponPasscodeDialog = false; + mCommissionerPasscode = false; + mCommissionerPasscodeReady = false; + mCancelPasscode = false; + mExpirationTime = System::Clock::kZero; + mUDCClientProcessingState = UDCClientProcessingState::kNotInitialized; + mCachedCommissionerPasscode = 0; } private: @@ -208,6 +212,8 @@ class UDCClientState UDCClientProcessingState mUDCClientProcessingState; System::Clock::Timestamp mExpirationTime = System::Clock::kZero; + + uint32_t mCachedCommissionerPasscode = 0; }; } // namespace UserDirectedCommissioning diff --git a/src/protocols/user_directed_commissioning/UserDirectedCommissioning.h b/src/protocols/user_directed_commissioning/UserDirectedCommissioning.h index d05a80a857f200..b4b7e90a085611 100644 --- a/src/protocols/user_directed_commissioning/UserDirectedCommissioning.h +++ b/src/protocols/user_directed_commissioning/UserDirectedCommissioning.h @@ -115,7 +115,7 @@ class DLL_EXPORT IdentificationDeclaration } return false; } - size_t GetNumTargetAppInfos() const { return mNumTargetAppInfos; } + uint8_t GetNumTargetAppInfos() const { return mNumTargetAppInfos; } bool AddTargetAppInfo(TargetAppInfo vid) { @@ -246,7 +246,7 @@ class DLL_EXPORT IdentificationDeclaration } if (mCommissionerPasscodeReady) { - ChipLogDetail(AppServer, "\ttcommissioner passcode ready: true"); + ChipLogDetail(AppServer, "\tcommissioner passcode ready: true"); } if (mCancelPasscode) { @@ -328,7 +328,9 @@ class DLL_EXPORT CommissionerDeclaration kAppInstallConsentPending = 13, kAppInstalling = 14, kAppInstallFailed = 15, - kAppInstalledRetryNeeded = 16 + kAppInstalledRetryNeeded = 16, + kCommissionerPasscodeDisabled = 17, + kUnexpectedCommissionerPasscodeReady = 18 }; constexpr static size_t kUdcTLVDataMaxBytes = 500; diff --git a/src/protocols/user_directed_commissioning/UserDirectedCommissioningClient.cpp b/src/protocols/user_directed_commissioning/UserDirectedCommissioningClient.cpp index 1d93ae5bb1d75e..3bb69c099612e3 100644 --- a/src/protocols/user_directed_commissioning/UserDirectedCommissioningClient.cpp +++ b/src/protocols/user_directed_commissioning/UserDirectedCommissioningClient.cpp @@ -132,27 +132,30 @@ uint32_t IdentificationDeclaration::WritePayload(uint8_t * payloadBuffer, size_t (err = writer.PutBytes(chip::TLV::ContextTag(kRotatingIdTag), mRotatingId, static_cast(mRotatingIdLen))), LogErrorOnFailure(err)); - // AppVendorIdList - VerifyOrExit( - CHIP_NO_ERROR == - (err = writer.StartContainer(chip::TLV::ContextTag(kTargetAppListTag), chip::TLV::kTLVType_List, listContainerType)), - LogErrorOnFailure(err)); - for (size_t i = 0; i < mNumTargetAppInfos; i++) + if (mNumTargetAppInfos > 0) { - // start the TargetApp structure + // AppVendorIdList VerifyOrExit(CHIP_NO_ERROR == - (err = writer.StartContainer(chip::TLV::ContextTag(kTargetAppTag), chip::TLV::kTLVType_Structure, - outerContainerType)), - LogErrorOnFailure(err)); - // add the vendor Id - VerifyOrExit(CHIP_NO_ERROR == (err = writer.Put(chip::TLV::ContextTag(kAppVendorIdTag), mTargetAppInfos[i].vendorId)), + (err = writer.StartContainer(chip::TLV::ContextTag(kTargetAppListTag), chip::TLV::kTLVType_List, + listContainerType)), LogErrorOnFailure(err)); - VerifyOrExit(CHIP_NO_ERROR == (err = writer.Put(chip::TLV::ContextTag(kAppProductIdTag), mTargetAppInfos[i].productId)), - LogErrorOnFailure(err)); - // end the TargetApp structure - VerifyOrExit(CHIP_NO_ERROR == (err = writer.EndContainer(outerContainerType)), LogErrorOnFailure(err)); + for (size_t i = 0; i < mNumTargetAppInfos; i++) + { + // start the TargetApp structure + VerifyOrExit(CHIP_NO_ERROR == + (err = writer.StartContainer(chip::TLV::ContextTag(kTargetAppTag), chip::TLV::kTLVType_Structure, + outerContainerType)), + LogErrorOnFailure(err)); + // add the vendor Id + VerifyOrExit(CHIP_NO_ERROR == (err = writer.Put(chip::TLV::ContextTag(kAppVendorIdTag), mTargetAppInfos[i].vendorId)), + LogErrorOnFailure(err)); + VerifyOrExit(CHIP_NO_ERROR == (err = writer.Put(chip::TLV::ContextTag(kAppProductIdTag), mTargetAppInfos[i].productId)), + LogErrorOnFailure(err)); + // end the TargetApp structure + VerifyOrExit(CHIP_NO_ERROR == (err = writer.EndContainer(outerContainerType)), LogErrorOnFailure(err)); + } + VerifyOrExit(CHIP_NO_ERROR == (err = writer.EndContainer(listContainerType)), LogErrorOnFailure(err)); } - VerifyOrExit(CHIP_NO_ERROR == (err = writer.EndContainer(listContainerType)), LogErrorOnFailure(err)); VerifyOrExit(CHIP_NO_ERROR == (err = writer.PutBoolean(chip::TLV::ContextTag(kNoPasscodeTag), mNoPasscode)), LogErrorOnFailure(err)); @@ -169,9 +172,10 @@ uint32_t IdentificationDeclaration::WritePayload(uint8_t * payloadBuffer, size_t VerifyOrExit(CHIP_NO_ERROR == (err = writer.EndContainer(outerContainerType)), LogErrorOnFailure(err)); VerifyOrExit(CHIP_NO_ERROR == (err = writer.Finalize()), LogErrorOnFailure(err)); - return writer.GetLengthWritten(); + return writer.GetLengthWritten() + static_cast(sizeof(mInstanceName)); exit: + ChipLogError(AppServer, "IdentificationDeclaration::WritePayload exiting early error %" CHIP_ERROR_FORMAT, err.Format()); return 0; } diff --git a/src/protocols/user_directed_commissioning/UserDirectedCommissioningServer.cpp b/src/protocols/user_directed_commissioning/UserDirectedCommissioningServer.cpp index 1a2f125ae5d282..746df853436877 100644 --- a/src/protocols/user_directed_commissioning/UserDirectedCommissioningServer.cpp +++ b/src/protocols/user_directed_commissioning/UserDirectedCommissioningServer.cpp @@ -327,6 +327,10 @@ CHIP_ERROR IdentificationDeclaration::ReadPayload(uint8_t * udcPayload, size_t p err = reader.Get(mCancelPasscode); break; } + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "IdentificationDeclaration::ReadPayload read error %" CHIP_ERROR_FORMAT, err.Format()); + } } if (err == CHIP_END_OF_TLV) @@ -334,6 +338,10 @@ CHIP_ERROR IdentificationDeclaration::ReadPayload(uint8_t * udcPayload, size_t p // Exiting container ReturnErrorOnFailure(reader.ExitContainer(outerContainerType)); } + else + { + ChipLogError(AppServer, "IdentificationDeclaration::ReadPayload exiting early error %" CHIP_ERROR_FORMAT, err.Format()); + } ChipLogProgress(AppServer, "UDC TLV parse complete"); return CHIP_NO_ERROR;