From 9327eaef6116d969797b0f5346e381575c3a189f Mon Sep 17 00:00:00 2001 From: Song GUO Date: Wed, 18 May 2022 09:26:07 +0800 Subject: [PATCH] [nwprov] Implement breadcrumb support (#18303) * [nwprov] Breadcrumb support * Add interface to generalcommissioning cluster to touch breadcrumb * Update * Rename UpdateBreadcrumb --- .../general-commissioning-server.cpp | 13 +++++ .../general-commissioning-server.h | 30 +++++++++++ .../network-commissioning.cpp | 54 +++++++++++++++++-- .../network-commissioning.h | 9 ++++ .../test_scripts/network_commissioning.py | 37 ++++++++++--- 5 files changed, 131 insertions(+), 12 deletions(-) create mode 100644 src/app/clusters/general-commissioning-server/general-commissioning-server.h diff --git a/src/app/clusters/general-commissioning-server/general-commissioning-server.cpp b/src/app/clusters/general-commissioning-server/general-commissioning-server.cpp index 5dbee6f93045eb..fa8d755fc23467 100644 --- a/src/app/clusters/general-commissioning-server/general-commissioning-server.cpp +++ b/src/app/clusters/general-commissioning-server/general-commissioning-server.cpp @@ -310,3 +310,16 @@ void MatterGeneralCommissioningPluginServerInitCallback() registerAttributeAccessOverride(&gAttrAccess); DeviceLayer::PlatformMgrImpl().AddEventHandler(OnPlatformEventHandler); } + +namespace chip { +namespace app { +namespace Clusters { +namespace GeneralCommissioning { +void SetBreadcrumb(Attributes::Breadcrumb::TypeInfo::Type breadcrumb) +{ + Breadcrumb::Set(0, breadcrumb); +} +} // namespace GeneralCommissioning +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/general-commissioning-server/general-commissioning-server.h b/src/app/clusters/general-commissioning-server/general-commissioning-server.h new file mode 100644 index 00000000000000..dc1cc8c7e23c63 --- /dev/null +++ b/src/app/clusters/general-commissioning-server/general-commissioning-server.h @@ -0,0 +1,30 @@ +/** + * + * Copyright (c) 2022 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 + +namespace chip { +namespace app { +namespace Clusters { +namespace GeneralCommissioning { + +void SetBreadcrumb(Attributes::Breadcrumb::TypeInfo::Type breadcrumb); + +} // namespace GeneralCommissioning +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/network-commissioning/network-commissioning.cpp b/src/app/clusters/network-commissioning/network-commissioning.cpp index 784a6c77ac6fb4..99c586f9c99678 100644 --- a/src/app/clusters/network-commissioning/network-commissioning.cpp +++ b/src/app/clusters/network-commissioning/network-commissioning.cpp @@ -18,9 +18,11 @@ #include "network-commissioning.h" +#include #include #include #include +#include #include #include #include @@ -267,12 +269,14 @@ void Instance::HandleScanNetworks(HandlerContext & ctx, const Commands::ScanNetw ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Protocols::InteractionModel::Status::InvalidCommand); return; } - mAsyncCommandHandle = CommandHandler::Handle(&ctx.mCommandHandler); + mCurrentOperationBreadcrumb = req.breadcrumb; + mAsyncCommandHandle = CommandHandler::Handle(&ctx.mCommandHandler); mpDriver.Get()->ScanNetworks(ssid, this); } else if (mFeatureFlags.Has(NetworkCommissioningFeature::kThreadNetworkInterface)) { - mAsyncCommandHandle = CommandHandler::Handle(&ctx.mCommandHandler); + mCurrentOperationBreadcrumb = req.breadcrumb; + mAsyncCommandHandle = CommandHandler::Handle(&ctx.mCommandHandler); mpDriver.Get()->ScanNetworks(this); } else @@ -362,6 +366,10 @@ void Instance::HandleAddOrUpdateWiFiNetwork(HandlerContext & ctx, const Commands mpDriver.Get()->AddOrUpdateNetwork(req.ssid, req.credentials, debugText, outNetworkIndex); FillDebugTextAndNetworkIndex(response, debugText, outNetworkIndex); ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + if (response.networkingStatus == NetworkCommissioningStatus::kSuccess) + { + UpdateBreadcrumb(req.breadcrumb); + } } void Instance::HandleAddOrUpdateThreadNetwork(HandlerContext & ctx, const Commands::AddOrUpdateThreadNetwork::DecodableType & req) @@ -381,6 +389,24 @@ void Instance::HandleAddOrUpdateThreadNetwork(HandlerContext & ctx, const Comman mpDriver.Get()->AddOrUpdateNetwork(req.operationalDataset, debugText, outNetworkIndex); FillDebugTextAndNetworkIndex(response, debugText, outNetworkIndex); ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + if (response.networkingStatus == NetworkCommissioningStatus::kSuccess) + { + UpdateBreadcrumb(req.breadcrumb); + } +} + +void Instance::UpdateBreadcrumb(const Optional & breadcrumb) +{ + VerifyOrReturn(breadcrumb.HasValue()); + GeneralCommissioning::SetBreadcrumb(breadcrumb.Value()); +} + +void Instance::CommitSavedBreadcrumb() +{ + // We rejected the command when there is another ongoing command, so mCurrentOperationBreadcrumb reflects the breadcrumb + // argument in the only background command. + UpdateBreadcrumb(mCurrentOperationBreadcrumb); + mCurrentOperationBreadcrumb.ClearValue(); } void Instance::HandleRemoveNetwork(HandlerContext & ctx, const Commands::RemoveNetwork::DecodableType & req) @@ -399,6 +425,10 @@ void Instance::HandleRemoveNetwork(HandlerContext & ctx, const Commands::RemoveN response.networkingStatus = mpWirelessDriver->RemoveNetwork(req.networkID, debugText, outNetworkIndex); FillDebugTextAndNetworkIndex(response, debugText, outNetworkIndex); ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + if (response.networkingStatus == NetworkCommissioningStatus::kSuccess) + { + UpdateBreadcrumb(req.breadcrumb); + } } void Instance::HandleConnectNetwork(HandlerContext & ctx, const Commands::ConnectNetwork::DecodableType & req) @@ -414,8 +444,8 @@ void Instance::HandleConnectNetwork(HandlerContext & ctx, const Commands::Connec mConnectingNetworkIDLen = static_cast(req.networkID.size()); memcpy(mConnectingNetworkID, req.networkID.data(), mConnectingNetworkIDLen); - - mAsyncCommandHandle = CommandHandler::Handle(&ctx.mCommandHandler); + mAsyncCommandHandle = CommandHandler::Handle(&ctx.mCommandHandler); + mCurrentOperationBreadcrumb = req.breadcrumb; mpWirelessDriver->ConnectNetwork(req.networkID, this); } @@ -431,6 +461,10 @@ void Instance::HandleReorderNetwork(HandlerContext & ctx, const Commands::Reorde response.networkingStatus = mpWirelessDriver->ReorderNetwork(req.networkID, req.networkIndex, debugText); FillDebugTextAndNetworkIndex(response, debugText, req.networkIndex); ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + if (response.networkingStatus == NetworkCommissioningStatus::kSuccess) + { + UpdateBreadcrumb(req.breadcrumb); + } } void Instance::OnResult(Status commissioningError, CharSpan debugText, int32_t interfaceStatus) @@ -467,6 +501,10 @@ void Instance::OnResult(Status commissioningError, CharSpan debugText, int32_t i mLastNetworkingStatusValue.SetNonNull(commissioningError); commandHandle->AddResponse(mPath, response); + if (commissioningError == NetworkCommissioningStatus::kSuccess) + { + CommitSavedBreadcrumb(); + } } void Instance::OnFinished(Status status, CharSpan debugText, ThreadScanResponseIterator * networks) @@ -529,6 +567,10 @@ void Instance::OnFinished(Status status, CharSpan debugText, ThreadScanResponseI { ChipLogError(Zcl, "Failed to encode response: %s", err.AsString()); } + if (status == NetworkCommissioningStatus::kSuccess) + { + CommitSavedBreadcrumb(); + } networks->Release(); } @@ -588,6 +630,10 @@ void Instance::OnFinished(Status status, CharSpan debugText, WiFiScanResponseIte { ChipLogError(Zcl, "Failed to encode response: %s", err.AsString()); } + if (status == NetworkCommissioningStatus::kSuccess) + { + CommitSavedBreadcrumb(); + } if (networks != nullptr) { networks->Release(); diff --git a/src/app/clusters/network-commissioning/network-commissioning.h b/src/app/clusters/network-commissioning/network-commissioning.h index 3edee1924b098e..fddd41098147c4 100644 --- a/src/app/clusters/network-commissioning/network-commissioning.h +++ b/src/app/clusters/network-commissioning/network-commissioning.h @@ -100,6 +100,15 @@ class Instance : public CommandHandlerInterface, uint8_t mLastNetworkID[DeviceLayer::NetworkCommissioning::kMaxNetworkIDLen]; uint8_t mLastNetworkIDLen = 0; + Optional mCurrentOperationBreadcrumb; + + // Commits the breadcrumb value saved in mCurrentOperationBreadcrumb to the breadcrumb attribute in GeneralCommissioning + // cluster. Will set mCurrentOperationBreadcrumb to NullOptional. + void CommitSavedBreadcrumb(); + + // Sets the breadcrumb attribute in GeneralCommissioning cluster, no-op when breadcrumbValue is NullOptional. + void UpdateBreadcrumb(const Optional & breadcrumbValue); + // Actual handlers of the commands void HandleScanNetworks(HandlerContext & ctx, const Commands::ScanNetworks::DecodableType & req); void HandleAddOrUpdateWiFiNetwork(HandlerContext & ctx, const Commands::AddOrUpdateWiFiNetwork::DecodableType & req); diff --git a/src/controller/python/test/test_scripts/network_commissioning.py b/src/controller/python/test/test_scripts/network_commissioning.py index 64f7f7fd7f8ffd..1b13928358c0a1 100644 --- a/src/controller/python/test/test_scripts/network_commissioning.py +++ b/src/controller/python/test/test_scripts/network_commissioning.py @@ -21,6 +21,7 @@ from chip.clusters.Types import NullValue import chip.interaction_model import asyncio +import random import base @@ -55,6 +56,18 @@ class NetworkCommissioningTests: def __init__(self, devCtrl, nodeid): self._devCtrl = devCtrl self._nodeid = nodeid + self._last_breadcrumb = random.randint(1, 1 << 48) + + async def must_verify_breadcrumb(self): + res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[(0, Clusters.GeneralCommissioning.Attributes.Breadcrumb)], returnClusterObject=True) + if self._last_breadcrumb is not None: + if self._last_breadcrumb != res[0][Clusters.GeneralCommissioning].breadcrumb: + raise AssertionError( + f"Breadcrumb attribute mismatch! Expect {self._last_breadcrumb} got {res[0][Clusters.GeneralCommissioning].breadcrumb}") + + def with_breadcrumb(self) -> int: + self._last_breadcrumb += 1 + return self._last_breadcrumb def log_interface_basic_info(self, values): logger.info(f"The interface supports {values.maxNetworks} networks.") @@ -129,11 +142,12 @@ async def test_wifi(self, endpointId): # Scan networks logger.info(f"Scan networks") req = Clusters.NetworkCommissioning.Commands.ScanNetworks( - ssid=b'', breadcrumb=0) + ssid=b'', breadcrumb=self.with_breadcrumb()) res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=endpointId, payload=req) logger.info(f"Received response: {res}") if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess: raise AssertionError(f"Unexpected result: {res.networkingStatus}") + await self.must_verify_breadcrumb() # Arm the failsafe before making network config changes logger.info(f"Arming the failsafe") @@ -149,17 +163,18 @@ async def test_wifi(self, endpointId): if len(networkList) != 0: logger.info(f"Removing existing network") req = Clusters.NetworkCommissioning.Commands.RemoveNetwork( - networkID=networkList[0].networkID, breadcrumb=0) + networkID=networkList[0].networkID, breadcrumb=self.with_breadcrumb()) res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=endpointId, payload=req) logger.info(f"Received response: {res}") if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess: raise AssertionError( f"Unexpected result: {res.networkingStatus}") + await self.must_verify_breadcrumb() # Add first network logger.info(f"Adding first test network") req = Clusters.NetworkCommissioning.Commands.AddOrUpdateWiFiNetwork( - ssid=TEST_WIFI_SSID.encode(), credentials=TEST_WIFI_PASS.encode(), breadcrumb=0) + ssid=TEST_WIFI_SSID.encode(), credentials=TEST_WIFI_PASS.encode(), breadcrumb=self.with_breadcrumb()) res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=endpointId, payload=req) logger.info(f"Received response: {res}") if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess: @@ -167,6 +182,7 @@ async def test_wifi(self, endpointId): if res.networkIndex != 0: raise AssertionError( f"Unexpected result: {res.networkIndex} (should be 0)") + await self.must_verify_breadcrumb() logger.info(f"Check network list") res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[(endpointId, Clusters.NetworkCommissioning.Attributes.Networks)], returnClusterObject=True) @@ -181,12 +197,13 @@ async def test_wifi(self, endpointId): logger.info(f"Connect to a network") req = Clusters.NetworkCommissioning.Commands.ConnectNetwork( - networkID=TEST_WIFI_SSID.encode(), breadcrumb=0) + networkID=TEST_WIFI_SSID.encode(), breadcrumb=self.with_breadcrumb()) res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=endpointId, payload=req) logger.info(f"Got response: {res}") if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess: raise AssertionError(f"Unexpected result: {res.networkingStatus}") logger.info(f"Device connected to a network.") + await self.must_verify_breadcrumb() # Disarm the failsafe logger.info(f"Disarming the failsafe") @@ -259,11 +276,12 @@ async def test_thread(self, endpointId): # Scan networks logger.info(f"Scan networks") req = Clusters.NetworkCommissioning.Commands.ScanNetworks( - ssid=b'', breadcrumb=0) + ssid=b'', breadcrumb=self.with_breadcrumb()) res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=endpointId, payload=req) logger.info(f"Received response: {res}") if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess: raise AssertionError(f"Unexpected result: {res.networkingStatus}") + await self.must_verify_breadcrumb() # Arm the failsafe before making network config changes logger.info(f"Arming the failsafe") @@ -279,17 +297,18 @@ async def test_thread(self, endpointId): if len(networkList) != 0: logger.info(f"Removing existing network") req = Clusters.NetworkCommissioning.Commands.RemoveNetwork( - networkID=networkList[0].networkID, breadcrumb=0) + networkID=networkList[0].networkID, breadcrumb=self.with_breadcrumb()) res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=endpointId, payload=req) logger.info(f"Received response: {res}") if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess: raise AssertionError( f"Unexpected result: {res.networkingStatus}") + await self.must_verify_breadcrumb() # Add first network logger.info(f"Adding first test network") req = Clusters.NetworkCommissioning.Commands.AddOrUpdateThreadNetwork( - operationalDataset=TEST_THREAD_NETWORK_DATASET_TLVS[0], breadcrumb=0) + operationalDataset=TEST_THREAD_NETWORK_DATASET_TLVS[0], breadcrumb=self.with_breadcrumb()) res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=endpointId, payload=req) logger.info(f"Received response: {res}") if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess: @@ -297,6 +316,7 @@ async def test_thread(self, endpointId): if res.networkIndex != 0: raise AssertionError( f"Unexpected result: {res.networkIndex} (should be 0)") + await self.must_verify_breadcrumb() logger.info(f"Check network list") res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[(endpointId, Clusters.NetworkCommissioning.Attributes.Networks)], returnClusterObject=True) @@ -311,12 +331,13 @@ async def test_thread(self, endpointId): logger.info(f"Connect to a network") req = Clusters.NetworkCommissioning.Commands.ConnectNetwork( - networkID=TEST_THREAD_NETWORK_IDS[0], breadcrumb=0) + networkID=TEST_THREAD_NETWORK_IDS[0], breadcrumb=self.with_breadcrumb()) res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=endpointId, payload=req) logger.info(f"Got response: {res}") if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess: raise AssertionError(f"Unexpected result: {res.networkingStatus}") logger.info(f"Device connected to a network.") + await self.must_verify_breadcrumb() # Disarm the failsafe logger.info(f"Disarming the failsafe")