Skip to content

Commit 9d413fd

Browse files
andy31415andreilitvinrestyled-commits
authored
Decouple network commissioning scan result encoding in separate source files (project-chip#39200)
* Separate out the scan response encoding from the single cpp file * Undo unintentional changes * Move load-responses as static/inline since this seems to save almost 100 bytes of flash on a NXP build * Add some silly unit tests (because we can) and double-protect against null pointers * Restyle * Look to fix tizen initializers * Look to fix tizen initializers * Rename stuff * Remove null network driver as it is not used by anything * Added conditional includes to for the encoding * Remove ScanResponseEncoder as these are old test files, not used * Added some lifetime data * Fix tests * Added better unit tests ... no functionality change though even though it feels odd... * Switch from RSS to LQI * Fix LQI ordering instead of RSSI * Restyle * Add vector include * Undid functional change * Added issue link * Restyled by clang-format --------- Co-authored-by: Andrei Litvin <andreilitvin@google.com> Co-authored-by: Restyled.io <commits@restyled.io>
1 parent e54ca8d commit 9d413fd

16 files changed

+865
-239
lines changed

src/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ if (chip_build_tests) {
5151
deps = []
5252
tests = [
5353
"${chip_root}/src/access/tests",
54+
"${chip_root}/src/app/clusters/network-commissioning/tests",
5455
"${chip_root}/src/app/clusters/ota-provider/tests",
5556
"${chip_root}/src/app/clusters/software-diagnostics-server/tests",
5657
"${chip_root}/src/app/cluster-building-blocks/tests",

src/app/clusters/network-commissioning/BUILD.gn

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,45 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14+
import("//build_overrides/build.gni")
15+
import("//build_overrides/chip.gni")
16+
17+
source_set("constants") {
18+
sources = [ "constants.h" ]
19+
}
20+
21+
source_set("wifi-response-encoding") {
22+
sources = [
23+
"WifiScanResponse.cpp",
24+
"WifiScanResponse.h",
25+
]
26+
27+
public_deps = [
28+
":constants",
29+
"${chip_root}/src/app/data-model",
30+
"${chip_root}/src/platform",
31+
"${chip_root}/zzz_generated/app-common/clusters/NetworkCommissioning",
32+
]
33+
}
34+
35+
source_set("thread-response-encoding") {
36+
sources = [
37+
"ThreadScanResponse.cpp",
38+
"ThreadScanResponse.h",
39+
]
40+
41+
public_deps = [
42+
":constants",
43+
"${chip_root}/src/app/data-model",
44+
"${chip_root}/src/lib/support",
45+
"${chip_root}/src/platform",
46+
"${chip_root}/zzz_generated/app-common/clusters/NetworkCommissioning",
47+
]
48+
}
49+
1450
group("network-commissioning") {
51+
public_deps = [
52+
":thread-response-encoding",
53+
":wifi-response-encoding",
54+
]
1555
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*
2+
* Copyright (c) 2021-2025 Project CHIP Authors
3+
* All rights reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
#include "ThreadScanResponse.h"
18+
19+
#include "constants.h"
20+
#include <clusters/NetworkCommissioning/Commands.h>
21+
#include <clusters/NetworkCommissioning/Structs.h>
22+
#include <lib/support/SortUtils.h>
23+
24+
using chip::DeviceLayer::NetworkCommissioning::ThreadScanResponse;
25+
26+
namespace chip {
27+
namespace app {
28+
namespace Clusters {
29+
namespace NetworkCommissioning {
30+
31+
namespace {
32+
33+
/// Fills up scanResponseArray with valid and de-duplicated thread responses from mNetworks.
34+
/// Handles sorting and keeping only larger rssi
35+
///
36+
/// Returns the valid list of scan responses into `validResponses`, which is only valid
37+
/// as long as scanResponseArray is valid.
38+
CHIP_ERROR LoadResponses(DeviceLayer::NetworkCommissioning::ThreadScanResponseIterator * networks,
39+
Platform::ScopedMemoryBuffer<ThreadScanResponse> & scanResponseArray,
40+
Span<ThreadScanResponse> & validResponses)
41+
{
42+
VerifyOrReturnError(scanResponseArray.Alloc(std::min(networks == nullptr ? 0 : networks->Count(), kMaxNetworksInScanResponse)),
43+
CHIP_ERROR_NO_MEMORY);
44+
45+
ThreadScanResponse scanResponse;
46+
size_t scanResponseArrayLength = 0;
47+
for (; networks != nullptr && networks->Next(scanResponse);)
48+
{
49+
if ((scanResponseArrayLength == kMaxNetworksInScanResponse) &&
50+
(scanResponseArray[scanResponseArrayLength - 1].rssi > scanResponse.rssi))
51+
{
52+
continue;
53+
}
54+
55+
bool isDuplicated = false;
56+
57+
for (size_t i = 0; i < scanResponseArrayLength; i++)
58+
{
59+
if ((scanResponseArray[i].panId == scanResponse.panId) &&
60+
(scanResponseArray[i].extendedPanId == scanResponse.extendedPanId))
61+
{
62+
if (scanResponseArray[i].rssi < scanResponse.rssi)
63+
{
64+
scanResponseArray[i] = scanResponseArray[--scanResponseArrayLength];
65+
}
66+
else
67+
{
68+
isDuplicated = true;
69+
}
70+
break;
71+
}
72+
}
73+
74+
if (isDuplicated)
75+
{
76+
continue;
77+
}
78+
79+
if (scanResponseArrayLength < kMaxNetworksInScanResponse)
80+
{
81+
scanResponseArrayLength++;
82+
}
83+
scanResponseArray[scanResponseArrayLength - 1] = scanResponse;
84+
85+
// TODO: this is a sort (insertion sort even, so O(n^2)) in a O(n) loop.
86+
/// There should be some better alternatives to not have some O(n^3) processing complexity.
87+
Sorting::InsertionSort(scanResponseArray.Get(), scanResponseArrayLength,
88+
[](const ThreadScanResponse & a, const ThreadScanResponse & b) -> bool { return a.rssi > b.rssi; });
89+
}
90+
91+
validResponses = Span<ThreadScanResponse>(scanResponseArray.Get(), scanResponseArrayLength);
92+
93+
return CHIP_NO_ERROR;
94+
}
95+
} // namespace
96+
97+
CHIP_ERROR ThreadScanResponseToTLV::EncodeTo(TLV::TLVWriter & writer, TLV::Tag tag) const
98+
{
99+
Platform::ScopedMemoryBuffer<ThreadScanResponse> responseArray;
100+
Span<ThreadScanResponse> responseSpan;
101+
102+
ReturnErrorOnFailure(LoadResponses(mNetworks, responseArray, responseSpan));
103+
104+
TLV::TLVType outerType;
105+
ReturnErrorOnFailure(writer.StartContainer(tag, TLV::kTLVType_Structure, outerType));
106+
107+
ReturnErrorOnFailure(writer.Put(TLV::ContextTag(Commands::ScanNetworksResponse::Fields::kNetworkingStatus), mStatus));
108+
if (mDebugText.size() != 0)
109+
{
110+
ReturnErrorOnFailure(
111+
DataModel::Encode(writer, TLV::ContextTag(Commands::ScanNetworksResponse::Fields::kDebugText), mDebugText));
112+
}
113+
114+
{
115+
TLV::TLVType listContainerType;
116+
ReturnErrorOnFailure(writer.StartContainer(TLV::ContextTag(Commands::ScanNetworksResponse::Fields::kThreadScanResults),
117+
TLV::kTLVType_Array, listContainerType));
118+
119+
for (const ThreadScanResponse & response : responseSpan)
120+
{
121+
Structs::ThreadInterfaceScanResultStruct::Type result;
122+
uint8_t extendedAddressBuffer[Thread::kSizeExtendedPanId];
123+
124+
Encoding::BigEndian::Put64(extendedAddressBuffer, response.extendedAddress);
125+
result.panId = response.panId;
126+
result.extendedPanId = response.extendedPanId;
127+
result.networkName = CharSpan(response.networkName, response.networkNameLen);
128+
result.channel = response.channel;
129+
result.version = response.version;
130+
result.extendedAddress = ByteSpan(extendedAddressBuffer);
131+
result.rssi = response.rssi;
132+
result.lqi = response.lqi;
133+
134+
ReturnErrorOnFailure(DataModel::Encode(writer, TLV::AnonymousTag(), result));
135+
}
136+
137+
ReturnErrorOnFailure(writer.EndContainer(listContainerType));
138+
}
139+
140+
return writer.EndContainer(outerType);
141+
}
142+
143+
} // namespace NetworkCommissioning
144+
} // namespace Clusters
145+
} // namespace app
146+
} // namespace chip
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright (c) 2021-2025 Project CHIP Authors
3+
* All rights reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
#pragma once
18+
19+
#include <app/data-model/EncodableToTLV.h>
20+
#include <clusters/NetworkCommissioning/Enums.h>
21+
#include <platform/NetworkCommissioning.h>
22+
23+
namespace chip {
24+
namespace app {
25+
namespace Clusters {
26+
namespace NetworkCommissioning {
27+
28+
/// Handles encoding a ThreadScanResponseIterator into a TLV response structure.
29+
class ThreadScanResponseToTLV : public chip::app::DataModel::EncodableToTLV
30+
{
31+
public:
32+
/// Object will iterate over `networks` during `EncodeTo`
33+
/// However it does NOT take ownership (expects caller to manage release)
34+
ThreadScanResponseToTLV(NetworkCommissioningStatusEnum status, CharSpan debugText,
35+
DeviceLayer::NetworkCommissioning::ThreadScanResponseIterator * networks) :
36+
mStatus(status),
37+
mDebugText(debugText), mNetworks(networks)
38+
{}
39+
40+
CHIP_ERROR EncodeTo(TLV::TLVWriter & writer, TLV::Tag tag) const override;
41+
42+
private:
43+
using ThreadScanResponse = DeviceLayer::NetworkCommissioning::ThreadScanResponse;
44+
45+
NetworkCommissioningStatusEnum mStatus;
46+
CharSpan mDebugText;
47+
48+
// User of class will release mNetworks, this class only uses it for
49+
// iterating
50+
DeviceLayer::NetworkCommissioning::ThreadScanResponseIterator * mNetworks;
51+
};
52+
53+
} // namespace NetworkCommissioning
54+
} // namespace Clusters
55+
} // namespace app
56+
} // namespace chip
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright (c) 2021-2025 Project CHIP Authors
3+
* All rights reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
#include "WifiScanResponse.h"
18+
19+
#include "constants.h"
20+
#include <clusters/NetworkCommissioning/Commands.h>
21+
#include <clusters/NetworkCommissioning/Structs.h>
22+
23+
using chip::DeviceLayer::NetworkCommissioning::WiFiScanResponse;
24+
25+
namespace chip {
26+
namespace app {
27+
namespace Clusters {
28+
namespace NetworkCommissioning {
29+
30+
CHIP_ERROR WifiScanResponseToTLV::EncodeTo(TLV::TLVWriter & writer, TLV::Tag tag) const
31+
{
32+
TLV::TLVType outerType;
33+
ReturnErrorOnFailure(writer.StartContainer(tag, TLV::kTLVType_Structure, outerType));
34+
35+
ReturnErrorOnFailure(writer.Put(TLV::ContextTag(Commands::ScanNetworksResponse::Fields::kNetworkingStatus), mStatus));
36+
if (mDebugText.size() != 0)
37+
{
38+
ReturnErrorOnFailure(
39+
DataModel::Encode(writer, TLV::ContextTag(Commands::ScanNetworksResponse::Fields::kDebugText), mDebugText));
40+
}
41+
42+
{
43+
TLV::TLVType listContainerType;
44+
ReturnErrorOnFailure(writer.StartContainer(TLV::ContextTag(Commands::ScanNetworksResponse::Fields::kWiFiScanResults),
45+
TLV::kTLVType_Array, listContainerType));
46+
47+
if ((mStatus == NetworkCommissioningStatusEnum::kSuccess) && (mNetworks != nullptr))
48+
{
49+
WiFiScanResponse scanResponse;
50+
size_t networksEncoded = 0;
51+
while (mNetworks->Next(scanResponse))
52+
{
53+
Structs::WiFiInterfaceScanResultStruct::Type result;
54+
result.security = scanResponse.security;
55+
result.ssid = ByteSpan(scanResponse.ssid, scanResponse.ssidLen);
56+
result.bssid = ByteSpan(scanResponse.bssid, sizeof(scanResponse.bssid));
57+
result.channel = scanResponse.channel;
58+
result.wiFiBand = scanResponse.wiFiBand;
59+
result.rssi = scanResponse.rssi;
60+
ReturnErrorOnFailure(DataModel::Encode(writer, TLV::AnonymousTag(), result));
61+
62+
++networksEncoded;
63+
if (networksEncoded >= kMaxNetworksInScanResponse)
64+
{
65+
break;
66+
}
67+
}
68+
}
69+
70+
ReturnErrorOnFailure(writer.EndContainer(listContainerType));
71+
}
72+
73+
return writer.EndContainer(outerType);
74+
}
75+
76+
} // namespace NetworkCommissioning
77+
} // namespace Clusters
78+
} // namespace app
79+
} // namespace chip
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright (c) 2021-2025 Project CHIP Authors
3+
* All rights reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
#pragma once
18+
19+
#include <app/data-model/EncodableToTLV.h>
20+
#include <clusters/NetworkCommissioning/Enums.h>
21+
#include <platform/NetworkCommissioning.h>
22+
23+
namespace chip {
24+
namespace app {
25+
namespace Clusters {
26+
namespace NetworkCommissioning {
27+
28+
/// Handles encoding a WifiScanResponseIterator into a TLV response structure
29+
class WifiScanResponseToTLV : public chip::app::DataModel::EncodableToTLV
30+
{
31+
public:
32+
/// Object will iterate over `networks` during `EncodeTo`
33+
/// However it does NOT take ownership (expects caller to manage release)
34+
WifiScanResponseToTLV(NetworkCommissioningStatusEnum status, CharSpan debugText,
35+
DeviceLayer::NetworkCommissioning::WiFiScanResponseIterator * networks) :
36+
mStatus(status),
37+
mDebugText(debugText), mNetworks(networks)
38+
{}
39+
40+
CHIP_ERROR EncodeTo(TLV::TLVWriter & writer, TLV::Tag tag) const override;
41+
42+
private:
43+
NetworkCommissioningStatusEnum mStatus;
44+
CharSpan mDebugText;
45+
46+
// User of class will release mNetworks, this class only uses it for
47+
// iterating
48+
DeviceLayer::NetworkCommissioning::WiFiScanResponseIterator * mNetworks;
49+
};
50+
51+
} // namespace NetworkCommissioning
52+
} // namespace Clusters
53+
} // namespace app
54+
} // namespace chip

0 commit comments

Comments
 (0)