[nwprov] Make it possible to notify network commissioning cluster for autonomous connection (#15666)
* [nwprov] Move GetLast* attributes to Driver side
* Add placeholder for other platforms
* Fix Linux ThreadStackManagerImpl
* Add issue number
* Fix
* Set LastNetworkingStatus for manual Scan operations
* Let the driver push result instead of cluster getting the value
diff --git a/src/app/clusters/network-commissioning/network-commissioning.cpp b/src/app/clusters/network-commissioning/network-commissioning.cpp
index e1ee0ac..3ae06ed 100644
--- a/src/app/clusters/network-commissioning/network-commissioning.cpp
+++ b/src/app/clusters/network-commissioning/network-commissioning.cpp
@@ -86,7 +86,7 @@
VerifyOrReturnError(registerAttributeAccessOverride(this), CHIP_ERROR_INCORRECT_STATE);
ReturnErrorOnFailure(
DeviceLayer::PlatformMgrImpl().AddEventHandler(_OnCommissioningComplete, reinterpret_cast<intptr_t>(this)));
- ReturnErrorOnFailure(mpBaseDriver->Init());
+ ReturnErrorOnFailure(mpBaseDriver->Init(this));
mLastNetworkingStatusValue.SetNull();
mLastConnectErrorValue.SetNull();
mLastNetworkIDLen = 0;
@@ -239,6 +239,34 @@
}
}
+void Instance::OnNetworkingStatusChange(DeviceLayer::NetworkCommissioning::Status aCommissioningError,
+ Optional<ByteSpan> aNetworkId, Optional<int32_t> aConnectStatus)
+{
+ if (aNetworkId.HasValue() && aNetworkId.Value().size() > kMaxNetworkIDLen)
+ {
+ ChipLogError(DeviceLayer, "Invalid network id received when calling OnNetworkingStatusChange");
+ return;
+ }
+ mLastNetworkingStatusValue.SetNonNull(ToClusterObjectEnum(aCommissioningError));
+ if (aNetworkId.HasValue())
+ {
+ memcpy(mLastNetworkID, aNetworkId.Value().data(), aNetworkId.Value().size());
+ mLastNetworkIDLen = static_cast<uint8_t>(aNetworkId.Value().size());
+ }
+ else
+ {
+ mLastNetworkIDLen = 0;
+ }
+ if (aConnectStatus.HasValue())
+ {
+ mLastConnectErrorValue.SetNonNull(aConnectStatus.Value());
+ }
+ else
+ {
+ mLastConnectErrorValue.SetNull();
+ }
+}
+
void Instance::HandleScanNetworks(HandlerContext & ctx, const Commands::ScanNetworks::DecodableType & req)
{
@@ -321,7 +349,15 @@
mLastNetworkIDLen = mConnectingNetworkIDLen;
memcpy(mLastNetworkID, mConnectingNetworkID, mLastNetworkIDLen);
mLastNetworkingStatusValue.SetNonNull(ToClusterObjectEnum(commissioningError));
- mLastConnectErrorValue.SetNonNull(interfaceStatus);
+
+ if (commissioningError == Status::kSuccess)
+ {
+ mLastConnectErrorValue.SetNull();
+ }
+ else
+ {
+ mLastConnectErrorValue.SetNonNull(interfaceStatus);
+ }
if (commissioningError == Status::kSuccess)
{
diff --git a/src/app/clusters/network-commissioning/network-commissioning.h b/src/app/clusters/network-commissioning/network-commissioning.h
index 40dfab9..7678765 100644
--- a/src/app/clusters/network-commissioning/network-commissioning.h
+++ b/src/app/clusters/network-commissioning/network-commissioning.h
@@ -36,6 +36,7 @@
// TODO: Use macro to disable some wifi or thread
class Instance : public CommandHandlerInterface,
public AttributeAccessInterface,
+ public DeviceLayer::NetworkCommissioning::Internal::BaseDriver::NetworkStatusChangeCallback,
public DeviceLayer::NetworkCommissioning::Internal::WirelessDriver::ConnectCallback,
public DeviceLayer::NetworkCommissioning::WiFiDriver::ScanCallback,
public DeviceLayer::NetworkCommissioning::ThreadDriver::ScanCallback
@@ -54,6 +55,10 @@
CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override;
CHIP_ERROR Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder) override;
+ // BaseDriver::NetworkStatusChangeCallback
+ void OnNetworkingStatusChange(DeviceLayer::NetworkCommissioning::Status aCommissioningError, Optional<ByteSpan> aNetworkId,
+ Optional<int32_t> aConnectStatus) override;
+
// WirelessDriver::ConnectCallback
void OnResult(DeviceLayer::NetworkCommissioning::Status commissioningError, CharSpan errorText,
int32_t interfaceStatus) override;
@@ -84,8 +89,9 @@
// Last* attributes
// Setting these values don't have to care about parallel requests, since we will reject other requests when there is another
// request ongoing.
+ // These values can be updated via OnNetworkingStatusChange callback, ScanCallback::OnFinished and ConnectCallback::OnResult.
DataModel::Nullable<NetworkCommissioningStatus> mLastNetworkingStatusValue;
- DataModel::Nullable<Attributes::LastConnectErrorValue::TypeInfo::Type> mLastConnectErrorValue;
+ Attributes::LastConnectErrorValue::TypeInfo::Type mLastConnectErrorValue;
uint8_t mConnectingNetworkID[DeviceLayer::NetworkCommissioning::kMaxNetworkIDLen];
uint8_t mConnectingNetworkIDLen = 0;
uint8_t mLastNetworkID[DeviceLayer::NetworkCommissioning::kMaxNetworkIDLen];
diff --git a/src/controller/python/test/test_scripts/network_commissioning.py b/src/controller/python/test/test_scripts/network_commissioning.py
index 92bba20..13f64bd 100644
--- a/src/controller/python/test/test_scripts/network_commissioning.py
+++ b/src/controller/python/test/test_scripts/network_commissioning.py
@@ -116,13 +116,6 @@
if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess:
raise AssertionError(f"Unexpected result: {res.networkingStatus}")
- # Verify Last* attributes
- logger.info(f"Read Last* attributes")
- res = await self.readLastNetworkingStateAttributes(endpointId=endpointId)
- if (res.lastNetworkID != NullValue) or (res.lastNetworkingStatus == NullValue) or (res.lastConnectErrorValue != NullValue):
- raise AssertionError(
- f"LastNetworkID and LastConnectErrorValue should be Null and LastNetworkingStatus should not be Null")
-
# Remove existing network
logger.info(f"Check network list")
res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[(endpointId, Clusters.NetworkCommissioning.Attributes.Networks)], returnClusterObject=True)
@@ -187,9 +180,9 @@
# Verify Last* attributes
logger.info(f"Read Last* attributes")
res = await self.readLastNetworkingStateAttributes(endpointId=endpointId)
- if (res.lastNetworkID == NullValue) or (res.lastNetworkingStatus == NullValue) or (res.lastConnectErrorValue == NullValue):
+ if (res.lastNetworkID == NullValue) or (res.lastNetworkingStatus == NullValue) or (res.lastConnectErrorValue != NullValue):
raise AssertionError(
- f"LastNetworkID, LastConnectErrorValue and LastNetworkingStatus should not be Null")
+ f"LastNetworkID, LastNetworkingStatus should not be Null, LastConnectErrorValue should be Null for a successful network provision.")
async def test_thread(self, endpointId):
logger.info(f"Get basic information of the endpoint")
@@ -221,13 +214,6 @@
if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess:
raise AssertionError(f"Unexpected result: {res.networkingStatus}")
- # Verify Last* attributes
- logger.info(f"Read Last* attributes")
- res = await self.readLastNetworkingStateAttributes(endpointId=endpointId)
- if (res.lastNetworkID != NullValue) or (res.lastNetworkingStatus == NullValue) or (res.lastConnectErrorValue != NullValue):
- raise AssertionError(
- f"LastNetworkID and LastConnectErrorValue should be Null and LastNetworkingStatus should not be Null")
-
# Remove existing network
logger.info(f"Check network list")
res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[(endpointId, Clusters.NetworkCommissioning.Attributes.Networks)], returnClusterObject=True)
@@ -275,9 +261,9 @@
# Verify Last* attributes
logger.info(f"Read Last* attributes")
res = await self.readLastNetworkingStateAttributes(endpointId=endpointId)
- if (res.lastNetworkID == NullValue) or (res.lastNetworkingStatus == NullValue) or (res.lastConnectErrorValue == NullValue):
+ if (res.lastNetworkID == NullValue) or (res.lastNetworkingStatus == NullValue) or (res.lastConnectErrorValue != NullValue):
raise AssertionError(
- f"LastNetworkID, LastConnectErrorValue and LastNetworkingStatus should not be Null")
+ f"LastNetworkID, LastNetworkingStatus should not be Null, LastConnectErrorValue should be Null for a successful network provision.")
logger.info(f"Check network list")
res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[(endpointId, Clusters.NetworkCommissioning.Attributes.Networks)], returnClusterObject=True)
diff --git a/src/include/platform/NetworkCommissioning.h b/src/include/platform/NetworkCommissioning.h
index 713c293..dc16a5e 100644
--- a/src/include/platform/NetworkCommissioning.h
+++ b/src/include/platform/NetworkCommissioning.h
@@ -25,6 +25,7 @@
#include <lib/core/CHIPCore.h>
#include <lib/core/CHIPSafeCasts.h>
+#include <lib/core/Optional.h>
#include <lib/support/ThreadOperationalDataset.h>
#include <platform/internal/DeviceNetworkInfo.h>
@@ -154,10 +155,26 @@
class BaseDriver
{
public:
+ class NetworkStatusChangeCallback
+ {
+ public:
+ /**
+ * @brief Callback for the network driver pushing the event of network status change to the network commissioning cluster.
+ * The platforms is explected to push the status from operations such as autonomous connection after loss of connectivity or
+ * during initial establishment.
+ *
+ * This function must be called in a thread-safe manner with CHIP stack.
+ */
+ virtual void OnNetworkingStatusChange(Status commissioningError, Optional<ByteSpan> networkId,
+ Optional<int32_t> connectStatus) = 0;
+
+ virtual ~NetworkStatusChangeCallback() = default;
+ };
+
/**
* @brief Initializes the driver, this function will be called when initializing the network commissioning cluster.
*/
- virtual CHIP_ERROR Init() { return CHIP_NO_ERROR; }
+ virtual CHIP_ERROR Init(NetworkStatusChangeCallback * networkStatusChangeCallback) { return CHIP_NO_ERROR; }
/**
* @brief Shuts down the driver, this function will be called when shutting down the network commissioning cluster.
diff --git a/src/platform/Ameba/NetworkCommissioningDriver.h b/src/platform/Ameba/NetworkCommissioningDriver.h
index a1430ad..0d8c4cb 100644
--- a/src/platform/Ameba/NetworkCommissioningDriver.h
+++ b/src/platform/Ameba/NetworkCommissioningDriver.h
@@ -89,7 +89,7 @@
// BaseDriver
NetworkIterator * GetNetworks() override { return new WiFiNetworkIterator(this); }
- CHIP_ERROR Init() override;
+ CHIP_ERROR Init(NetworkStatusChangeCallback * networkStatusChangeCallback) override;
CHIP_ERROR Shutdown() override;
// WirelessDriver
diff --git a/src/platform/Ameba/NetworkCommissioningWiFiDriver.cpp b/src/platform/Ameba/NetworkCommissioningWiFiDriver.cpp
index 25dd5a0..30f07d2 100644
--- a/src/platform/Ameba/NetworkCommissioningWiFiDriver.cpp
+++ b/src/platform/Ameba/NetworkCommissioningWiFiDriver.cpp
@@ -35,7 +35,7 @@
constexpr char kWiFiCredentialsKeyName[] = "wifi-pass";
} // namespace
-CHIP_ERROR AmebaWiFiDriver::Init()
+CHIP_ERROR AmebaWiFiDriver::Init(NetworkStatusChangeCallback *)
{
CHIP_ERROR err;
size_t ssidLen = 0;
diff --git a/src/platform/EFR32/NetworkCommissioningWiFiDriver.cpp b/src/platform/EFR32/NetworkCommissioningWiFiDriver.cpp
index d4c3948..1401b41 100644
--- a/src/platform/EFR32/NetworkCommissioningWiFiDriver.cpp
+++ b/src/platform/EFR32/NetworkCommissioningWiFiDriver.cpp
@@ -35,7 +35,7 @@
SlScanResponseIterator<NetworkCommissioning::WiFiScanResponse> mScanResponseIter(sScanResult);
} // namespace
-CHIP_ERROR SlWiFiDriver::Init()
+CHIP_ERROR SlWiFiDriver::Init(NetworkStatusChangeCallback * networkStatusChangeCallback)
{
CHIP_ERROR err;
size_t ssidLen = 0;
diff --git a/src/platform/EFR32/NetworkCommissioningWiFiDriver.h b/src/platform/EFR32/NetworkCommissioningWiFiDriver.h
index 6f41181..9a98a10 100644
--- a/src/platform/EFR32/NetworkCommissioningWiFiDriver.h
+++ b/src/platform/EFR32/NetworkCommissioningWiFiDriver.h
@@ -132,7 +132,7 @@
// BaseDriver
NetworkIterator * GetNetworks() override { return new WiFiNetworkIterator(this); }
- CHIP_ERROR Init() override;
+ CHIP_ERROR Init(NetworkStatusChangeCallback * networkStatusChangeCallback) override;
// WirelessDriver
uint8_t GetMaxNetworks() override { return kMaxWiFiNetworks; }
diff --git a/src/platform/ESP32/NetworkCommissioningDriver.h b/src/platform/ESP32/NetworkCommissioningDriver.h
index f998eb9..decc8e2 100644
--- a/src/platform/ESP32/NetworkCommissioningDriver.h
+++ b/src/platform/ESP32/NetworkCommissioningDriver.h
@@ -88,7 +88,7 @@
// BaseDriver
NetworkIterator * GetNetworks() override { return new WiFiNetworkIterator(this); }
- CHIP_ERROR Init() override;
+ CHIP_ERROR Init(NetworkStatusChangeCallback * networkStatusChangeCallback) override;
CHIP_ERROR Shutdown() override;
// WirelessDriver
diff --git a/src/platform/ESP32/NetworkCommissioningWiFiDriver.cpp b/src/platform/ESP32/NetworkCommissioningWiFiDriver.cpp
index 362de42..1e4fc0f 100644
--- a/src/platform/ESP32/NetworkCommissioningWiFiDriver.cpp
+++ b/src/platform/ESP32/NetworkCommissioningWiFiDriver.cpp
@@ -38,7 +38,7 @@
static uint8_t WiFiSSIDStr[DeviceLayer::Internal::kMaxWiFiSSIDLength];
} // namespace
-CHIP_ERROR ESPWiFiDriver::Init()
+CHIP_ERROR ESPWiFiDriver::Init(NetworkStatusChangeCallback * networkStatusChangeCallback)
{
CHIP_ERROR err;
size_t ssidLen = 0;
diff --git a/src/platform/Linux/ConnectivityManagerImpl.cpp b/src/platform/Linux/ConnectivityManagerImpl.cpp
index 8b8f305..c8ae0a7 100644
--- a/src/platform/Linux/ConnectivityManagerImpl.cpp
+++ b/src/platform/Linux/ConnectivityManagerImpl.cpp
@@ -335,6 +335,32 @@
DeviceLayer::SystemLayer().ScheduleWork(DriveAPState, NULL);
}
+void ConnectivityManagerImpl::UpdateNetworkStatus()
+{
+ Network configuredNetwork;
+
+ VerifyOrReturn(IsWiFiStationEnabled() && mpStatusChangeCallback != nullptr);
+
+ CHIP_ERROR err = GetConfiguredNetwork(configuredNetwork);
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(DeviceLayer, "Failed to get configured network when updating network status: %s", err.AsString());
+ return;
+ }
+
+ // If we have already connected to the WiFi AP, then return null to indicate a success state.
+ if (IsWiFiStationConnected())
+ {
+ mpStatusChangeCallback->OnNetworkingStatusChange(
+ Status::kSuccess, MakeOptional(ByteSpan(configuredNetwork.networkID, configuredNetwork.networkIDLen)), NullOptional);
+ return;
+ }
+
+ mpStatusChangeCallback->OnNetworkingStatusChange(
+ Status::kUnknownError, MakeOptional(ByteSpan(configuredNetwork.networkID, configuredNetwork.networkIDLen)),
+ MakeOptional(GetDisconnectReason()));
+}
+
void ConnectivityManagerImpl::_OnWpaPropertiesChanged(WpaFiW1Wpa_supplicant1Interface * proxy, GVariant * changed_properties,
const gchar * const * invalidated_properties, gpointer user_data)
{
@@ -400,6 +426,8 @@
delegate->OnAssociationFailureDetected(associationFailureCause, status);
}
+ DeviceLayer::SystemLayer().ScheduleLambda([]() { ConnectivityMgrImpl().UpdateNetworkStatus(); });
+
mAssociattionStarted = false;
}
else if (g_strcmp0(value_str, "\'associated\'") == 0)
@@ -408,6 +436,8 @@
{
delegate->OnConnectionStatusChanged(static_cast<uint8_t>(WiFiConnectionStatus::kNotConnected));
}
+
+ DeviceLayer::SystemLayer().ScheduleLambda([]() { ConnectivityMgrImpl().UpdateNetworkStatus(); });
}
}
@@ -1277,13 +1307,30 @@
return CHIP_NO_ERROR;
}
-CHIP_ERROR ConnectivityManagerImpl::GetConnectedNetwork(NetworkCommissioning::Network & network)
+int32_t ConnectivityManagerImpl::GetDisconnectReason()
+{
+ std::lock_guard<std::mutex> lock(mWpaSupplicantMutex);
+ std::unique_ptr<GError, GErrorDeleter> err;
+
+ gint errorValue = wpa_fi_w1_wpa_supplicant1_interface_get_disconnect_reason(mWpaSupplicant.iface);
+ // wpa_supplicant DBus API: DisconnectReason: The most recent IEEE 802.11 reason code for disconnect. Negative value
+ // indicates locally generated disconnection.
+ return errorValue;
+}
+
+CHIP_ERROR ConnectivityManagerImpl::GetConfiguredNetwork(NetworkCommissioning::Network & network)
{
std::lock_guard<std::mutex> lock(mWpaSupplicantMutex);
std::unique_ptr<GError, GErrorDeleter> err;
const gchar * networkPath = wpa_fi_w1_wpa_supplicant1_interface_get_current_network(mWpaSupplicant.iface);
+ // wpa_supplicant DBus API: if network path of current network is "/", means no networks is currently selected.
+ if (strcmp(networkPath, "/") == 0)
+ {
+ return CHIP_ERROR_KEY_NOT_FOUND;
+ }
+
std::unique_ptr<WpaFiW1Wpa_supplicant1Network, GObjectDeleter> networkInfo(
wpa_fi_w1_wpa_supplicant1_network_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE,
kWpaSupplicantServiceName, networkPath, nullptr,
diff --git a/src/platform/Linux/ConnectivityManagerImpl.h b/src/platform/Linux/ConnectivityManagerImpl.h
index 555aa2a..1a8c1dd 100644
--- a/src/platform/Linux/ConnectivityManagerImpl.h
+++ b/src/platform/Linux/ConnectivityManagerImpl.h
@@ -114,6 +114,11 @@
public:
#if CHIP_DEVICE_CONFIG_ENABLE_WPA
CHIP_ERROR ProvisionWiFiNetwork(const char * ssid, const char * key);
+ void
+ SetNetworkStatusChangeCallback(NetworkCommissioning::Internal::BaseDriver::NetworkStatusChangeCallback * statusChangeCallback)
+ {
+ mpStatusChangeCallback = statusChangeCallback;
+ }
CHIP_ERROR ConnectWiFiNetworkAsync(ByteSpan ssid, ByteSpan credentials,
NetworkCommissioning::Internal::WirelessDriver::ConnectCallback * connectCallback);
void PostNetworkConnect();
@@ -122,10 +127,11 @@
void StartWiFiManagement();
bool IsWiFiManagementStarted();
+ int32_t GetDisconnectReason();
CHIP_ERROR GetWiFiBssId(ByteSpan & value);
CHIP_ERROR GetWiFiSecurityType(uint8_t & securityType);
CHIP_ERROR GetWiFiVersion(uint8_t & wiFiVersion);
- CHIP_ERROR GetConnectedNetwork(NetworkCommissioning::Network & network);
+ CHIP_ERROR GetConfiguredNetwork(NetworkCommissioning::Network & network);
CHIP_ERROR StartWiFiScan(ByteSpan ssid, NetworkCommissioning::WiFiDriver::ScanCallback * callback);
#endif
@@ -174,6 +180,7 @@
void _MaintainOnDemandWiFiAP();
System::Clock::Timeout _GetWiFiAPIdleTimeout();
void _SetWiFiAPIdleTimeout(System::Clock::Timeout val);
+ void UpdateNetworkStatus();
static CHIP_ERROR StopAutoScan();
static void _OnWpaProxyReady(GObject * source_object, GAsyncResult * res, gpointer user_data);
@@ -193,6 +200,8 @@
static BitFlags<ConnectivityFlags> mConnectivityFlag;
static struct GDBusWpaSupplicant mWpaSupplicant;
static std::mutex mWpaSupplicantMutex;
+
+ NetworkCommissioning::Internal::BaseDriver::NetworkStatusChangeCallback * mpStatusChangeCallback = nullptr;
#endif
// ==================== ConnectivityManager Private Methods ====================
diff --git a/src/platform/Linux/NetworkCommissioningDriver.h b/src/platform/Linux/NetworkCommissioningDriver.h
index 41daa19..19431e5 100644
--- a/src/platform/Linux/NetworkCommissioningDriver.h
+++ b/src/platform/Linux/NetworkCommissioningDriver.h
@@ -79,8 +79,8 @@
// BaseDriver
NetworkIterator * GetNetworks() override { return new WiFiNetworkIterator(this); }
- CHIP_ERROR Init() override;
- CHIP_ERROR Shutdown() override { return CHIP_NO_ERROR; } // Nothing to do on linux for shutdown.
+ CHIP_ERROR Init(BaseDriver::NetworkStatusChangeCallback * networkStatusChangeCallback) override;
+ CHIP_ERROR Shutdown() override;
// WirelessDriver
uint8_t GetMaxNetworks() override { return 1; }
@@ -104,6 +104,7 @@
WiFiNetworkIterator mWiFiIterator = WiFiNetworkIterator(this);
WiFiNetwork mSavedNetwork;
WiFiNetwork mStagingNetwork;
+ Optional<Status> mScanStatus;
};
#endif // CHIP_DEVICE_CONFIG_ENABLE_WPA
@@ -127,8 +128,8 @@
// BaseDriver
NetworkIterator * GetNetworks() override { return new ThreadNetworkIterator(this); }
- CHIP_ERROR Init() override;
- CHIP_ERROR Shutdown() override { return CHIP_NO_ERROR; } // Nothing to do on linux for shutdown.
+ CHIP_ERROR Init(BaseDriver::NetworkStatusChangeCallback * networkStatusChangeCallback) override;
+ CHIP_ERROR Shutdown() override;
// WirelessDriver
uint8_t GetMaxNetworks() override { return 1; }
@@ -144,7 +145,7 @@
// ThreadDriver
Status AddOrUpdateNetwork(ByteSpan operationalDataset) override;
- void ScanNetworks(ScanCallback * callback) override;
+ void ScanNetworks(ThreadDriver::ScanCallback * callback) override;
private:
ThreadNetworkIterator mThreadIterator = ThreadNetworkIterator(this);
diff --git a/src/platform/Linux/NetworkCommissioningThreadDriver.cpp b/src/platform/Linux/NetworkCommissioningThreadDriver.cpp
index abbeeaa..2ba9c20 100644
--- a/src/platform/Linux/NetworkCommissioningThreadDriver.cpp
+++ b/src/platform/Linux/NetworkCommissioningThreadDriver.cpp
@@ -41,7 +41,7 @@
// TODO: The otbr-posix does not actually maintains its own networking states, it will always persist the last network connected.
// This should not be an issue for most cases, but we should implement the code for maintaining the states by ourselves.
-CHIP_ERROR LinuxThreadDriver::Init()
+CHIP_ERROR LinuxThreadDriver::Init(BaseDriver::NetworkStatusChangeCallback * networkStatusChangeCallback)
{
ByteSpan currentProvision;
VerifyOrReturnError(ConnectivityMgrImpl().IsThreadAttached(), CHIP_NO_ERROR);
@@ -50,6 +50,14 @@
mSavedNetwork.Init(currentProvision);
mStagingNetwork.Init(currentProvision);
+ ThreadStackMgrImpl().SetNetworkStatusChangeCallback(networkStatusChangeCallback);
+
+ return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR LinuxThreadDriver::Shutdown()
+{
+ ThreadStackMgrImpl().SetNetworkStatusChangeCallback(nullptr);
return CHIP_NO_ERROR;
}
@@ -148,6 +156,7 @@
void LinuxThreadDriver::ScanNetworks(ThreadDriver::ScanCallback * callback)
{
CHIP_ERROR err = DeviceLayer::ThreadStackMgrImpl().StartThreadScan(callback);
+ // The ThreadScan callback will always be invoked in CHIP mainloop, which is strictly after this function
if (err != CHIP_NO_ERROR)
{
callback->OnFinished(Status::kUnknownError, CharSpan(), nullptr);
diff --git a/src/platform/Linux/NetworkCommissioningWiFiDriver.cpp b/src/platform/Linux/NetworkCommissioningWiFiDriver.cpp
index ae56e1a..47ae498 100644
--- a/src/platform/Linux/NetworkCommissioningWiFiDriver.cpp
+++ b/src/platform/Linux/NetworkCommissioningWiFiDriver.cpp
@@ -50,7 +50,7 @@
// NOTE: For now, the LinuxWiFiDriver only supports one network, this can be fixed by using the wpa_supplicant API directly (then
// wpa_supplicant will manage the networks for us.)
-CHIP_ERROR LinuxWiFiDriver::Init()
+CHIP_ERROR LinuxWiFiDriver::Init(BaseDriver::NetworkStatusChangeCallback * networkStatusChangeCallback)
{
CHIP_ERROR err;
size_t ssidLen = 0;
@@ -73,6 +73,14 @@
mSavedNetwork.ssidLen = ssidLen;
mStagingNetwork = mSavedNetwork;
+
+ ConnectivityMgrImpl().SetNetworkStatusChangeCallback(networkStatusChangeCallback);
+ return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR LinuxWiFiDriver::Shutdown()
+{
+ ConnectivityMgrImpl().SetNetworkStatusChangeCallback(nullptr);
return CHIP_NO_ERROR;
}
@@ -166,8 +174,14 @@
CHIP_ERROR err = DeviceLayer::ConnectivityMgrImpl().StartWiFiScan(ssid, callback);
if (err != CHIP_NO_ERROR)
{
+ mScanStatus.SetValue(Status::kUnknownError);
callback->OnFinished(Status::kUnknownError, CharSpan(), nullptr);
}
+ else
+ {
+ // On linux platform, once "scan" is started, we can say the result will always be success.
+ mScanStatus.SetValue(Status::kSuccess);
+ }
}
size_t LinuxWiFiDriver::WiFiNetworkIterator::Count()
@@ -186,12 +200,12 @@
item.connected = false;
exhausted = true;
- Network connectedNetwork;
- CHIP_ERROR err = DeviceLayer::ConnectivityMgrImpl().GetConnectedNetwork(connectedNetwork);
+ Network configuredNetwork;
+ CHIP_ERROR err = DeviceLayer::ConnectivityMgrImpl().GetConfiguredNetwork(configuredNetwork);
if (err == CHIP_NO_ERROR)
{
- if (connectedNetwork.networkIDLen == item.networkIDLen &&
- memcmp(connectedNetwork.networkID, item.networkID, item.networkIDLen) == 0)
+ if (DeviceLayer::ConnectivityMgrImpl().IsWiFiStationConnected() && configuredNetwork.networkIDLen == item.networkIDLen &&
+ memcmp(configuredNetwork.networkID, item.networkID, item.networkIDLen) == 0)
{
item.connected = true;
}
diff --git a/src/platform/Linux/ThreadStackManagerImpl.cpp b/src/platform/Linux/ThreadStackManagerImpl.cpp
index 48e0ccd..02d89ca 100644
--- a/src/platform/Linux/ThreadStackManagerImpl.cpp
+++ b/src/platform/Linux/ThreadStackManagerImpl.cpp
@@ -94,6 +94,8 @@
if (key == nullptr || value == nullptr)
continue;
// ownership of key and value is still holding by the iter
+ DeviceLayer::SystemLayer().ScheduleLambda([me]() { me->_UpdateNetworkStatus(); });
+
if (strcmp(key, kPropertyDeviceRole) == 0)
{
const gchar * value_str = g_variant_get_string(value, nullptr);
@@ -301,7 +303,7 @@
std::unique_ptr<GError, GErrorDeleter> err;
- std::unique_ptr<GVariant, GVariantDeleter> value(
+ std::unique_ptr<GVariant, GVariantDeleter> response(
g_dbus_proxy_call_sync(G_DBUS_PROXY(mProxy.get()), "org.freedesktop.DBus.Properties.Get",
g_variant_new("(ss)", "io.openthread.BorderRouter", "DeviceRole"), G_DBUS_CALL_FLAGS_NONE, -1,
nullptr, &MakeUniquePointerReceiver(err).Get()));
@@ -312,6 +314,20 @@
return false;
}
+ if (response == nullptr)
+ {
+ return false;
+ }
+
+ std::unique_ptr<GVariant, GVariantDeleter> tupleContent(g_variant_get_child_value(response.get(), 0));
+
+ if (tupleContent == nullptr)
+ {
+ return false;
+ }
+
+ std::unique_ptr<GVariant, GVariantDeleter> value(g_variant_get_variant(tupleContent.get()));
+
if (value == nullptr)
{
return false;
@@ -319,6 +335,11 @@
const gchar * role = g_variant_get_string(value.get(), nullptr);
+ if (role == nullptr)
+ {
+ return false;
+ }
+
return (strcmp(role, kOpenthreadDeviceRoleDisabled) != 0);
}
@@ -692,6 +713,39 @@
return CHIP_NO_ERROR;
}
+void ThreadStackManagerImpl::_UpdateNetworkStatus()
+{
+ // Thread is not enabled, then we are not trying to connect to the network.
+ VerifyOrReturn(IsThreadEnabled() && mpStatusChangeCallback != nullptr);
+
+ ByteSpan datasetTLV;
+ Thread::OperationalDataset dataset;
+ uint8_t extpanid[Thread::kSizeExtendedPanId];
+
+ // If we have not provisioned any Thread network, return the status from last network scan,
+ // If we have provisioned a network, we assume the ot-br-posix is activitely connecting to that network.
+ CHIP_ERROR err = ThreadStackMgrImpl().GetThreadProvision(datasetTLV);
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(DeviceLayer, "Failed to get configured network when updating network status: %s", err.AsString());
+ return;
+ }
+
+ VerifyOrReturn(dataset.Init(datasetTLV) == CHIP_NO_ERROR);
+ // The Thread network is not enabled, but has a different extended pan id.
+ VerifyOrReturn(dataset.GetExtendedPanId(extpanid) == CHIP_NO_ERROR);
+
+ // We have already connected to the network, thus return success.
+ if (ThreadStackMgrImpl().IsThreadAttached())
+ {
+ mpStatusChangeCallback->OnNetworkingStatusChange(Status::kSuccess, MakeOptional(ByteSpan(extpanid)), NullOptional);
+ }
+ else
+ {
+ mpStatusChangeCallback->OnNetworkingStatusChange(Status::kNetworkNotFound, MakeOptional(ByteSpan(extpanid)), NullOptional);
+ }
+}
+
ThreadStackManager & ThreadStackMgr()
{
return chip::DeviceLayer::ThreadStackManagerImpl::sInstance;
diff --git a/src/platform/Linux/ThreadStackManagerImpl.h b/src/platform/Linux/ThreadStackManagerImpl.h
index e725bb5..4ae2fc7 100644
--- a/src/platform/Linux/ThreadStackManagerImpl.h
+++ b/src/platform/Linux/ThreadStackManagerImpl.h
@@ -36,6 +36,12 @@
public:
ThreadStackManagerImpl();
+ void
+ SetNetworkStatusChangeCallback(NetworkCommissioning::Internal::BaseDriver::NetworkStatusChangeCallback * statusChangeCallback)
+ {
+ mpStatusChangeCallback = statusChangeCallback;
+ }
+
CHIP_ERROR _InitThreadStack();
void _ProcessThreadActivity();
@@ -71,6 +77,8 @@
void _OnThreadAttachFinished(void);
+ void _UpdateNetworkStatus();
+
static void _OnThreadBrAttachFinished(GObject * source_object, GAsyncResult * res, gpointer user_data);
ConnectivityManager::ThreadDeviceType _GetThreadDeviceType();
@@ -144,6 +152,7 @@
NetworkCommissioning::ThreadDriver::ScanCallback * mpScanCallback;
NetworkCommissioning::Internal::WirelessDriver::ConnectCallback * mpConnectCallback;
+ NetworkCommissioning::Internal::BaseDriver::NetworkStatusChangeCallback * mpStatusChangeCallback = nullptr;
bool mAttached;
};
diff --git a/src/platform/OpenThread/GenericNetworkCommissioningThreadDriver.cpp b/src/platform/OpenThread/GenericNetworkCommissioningThreadDriver.cpp
index ccb3a40..717bc02 100644
--- a/src/platform/OpenThread/GenericNetworkCommissioningThreadDriver.cpp
+++ b/src/platform/OpenThread/GenericNetworkCommissioningThreadDriver.cpp
@@ -35,9 +35,11 @@
// load the network config from thread persistent info, and loads it into both mSavedNetwork and mStagingNetwork. When updating the
// networks, all changes are made on the staging network. When validated we can commit it and save it to the persistent info
-CHIP_ERROR GenericThreadDriver::Init()
+CHIP_ERROR GenericThreadDriver::Init(Internal::BaseDriver::NetworkStatusChangeCallback * statusChangeCallback)
{
ByteSpan currentProvision;
+ ThreadStackMgrImpl().SetNetworkStatusChangeCallback(statusChangeCallback);
+
VerifyOrReturnError(ThreadStackMgrImpl().IsThreadAttached(), CHIP_NO_ERROR);
VerifyOrReturnError(ThreadStackMgrImpl().GetThreadProvision(currentProvision) == CHIP_NO_ERROR, CHIP_NO_ERROR);
@@ -47,6 +49,12 @@
return CHIP_NO_ERROR;
}
+CHIP_ERROR GenericThreadDriver::Shutdown()
+{
+ ThreadStackMgrImpl().SetNetworkStatusChangeCallback(nullptr);
+ return CHIP_NO_ERROR;
+}
+
CHIP_ERROR GenericThreadDriver::CommitConfiguration()
{
// Note: on AttachToThreadNetwork OpenThread will persist the networks on its own,
@@ -150,8 +158,14 @@
CHIP_ERROR err = DeviceLayer::ThreadStackMgrImpl().StartThreadScan(callback);
if (err != CHIP_NO_ERROR)
{
+ mScanStatus.SetValue(Status::kUnknownError);
callback->OnFinished(Status::kUnknownError, CharSpan(), nullptr);
}
+ else
+ {
+ // OpenThread's "scan" will always success once started, so we can set the value of scan result here.
+ mScanStatus.SetValue(Status::kSuccess);
+ }
}
size_t GenericThreadDriver::ThreadNetworkIterator::Count()
diff --git a/src/platform/OpenThread/GenericNetworkCommissioningThreadDriver.h b/src/platform/OpenThread/GenericNetworkCommissioningThreadDriver.h
index be5ad93..ec801c9 100644
--- a/src/platform/OpenThread/GenericNetworkCommissioningThreadDriver.h
+++ b/src/platform/OpenThread/GenericNetworkCommissioningThreadDriver.h
@@ -84,8 +84,8 @@
// BaseDriver
NetworkIterator * GetNetworks() override { return new ThreadNetworkIterator(this); }
- CHIP_ERROR Init() override;
- CHIP_ERROR Shutdown() override { return CHIP_NO_ERROR; } // Nothing to do on EFR32 for shutdown.
+ CHIP_ERROR Init(Internal::BaseDriver::NetworkStatusChangeCallback * statusChangeCallback) override;
+ CHIP_ERROR Shutdown() override;
// WirelessDriver
uint8_t GetMaxNetworks() override { return 1; }
@@ -101,12 +101,13 @@
// ThreadDriver
Status AddOrUpdateNetwork(ByteSpan operationalDataset) override;
- void ScanNetworks(ScanCallback * callback) override;
+ void ScanNetworks(ThreadDriver::ScanCallback * callback) override;
private:
ThreadNetworkIterator mThreadIterator = ThreadNetworkIterator(this);
Thread::OperationalDataset mSavedNetwork = {};
Thread::OperationalDataset mStagingNetwork = {};
+ Optional<Status> mScanStatus;
};
} // namespace NetworkCommissioning
diff --git a/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.cpp b/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.cpp
index 039c1e7..ffbfa33 100644
--- a/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.cpp
+++ b/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.cpp
@@ -127,6 +127,8 @@
{
ChipLogError(DeviceLayer, "Failed to post Thread state change: %" CHIP_ERROR_FORMAT, status.Format());
}
+
+ DeviceLayer::SystemLayer().ScheduleLambda([]() { ThreadStackMgrImpl()._UpdateNetworkStatus(); });
}
template <class ImplClass>
@@ -1843,6 +1845,36 @@
return error;
}
+template <class ImplClass>
+void GenericThreadStackManagerImpl_OpenThread<ImplClass>::_UpdateNetworkStatus()
+{
+ // Thread is not enabled, then we are not trying to connect to the network.
+ VerifyOrReturn(ThreadStackMgrImpl().IsThreadEnabled() && mpStatusChangeCallback != nullptr);
+
+ ByteSpan datasetTLV;
+ Thread::OperationalDataset dataset;
+ uint8_t extpanid[chip::Thread::kSizeExtendedPanId];
+
+ // If we have not provisioned any Thread network, return the status from last network scan,
+ // If we have provisioned a network, we assume the ot-br-posix is activitely connecting to that network.
+ ReturnOnFailure(ThreadStackMgrImpl().GetThreadProvision(datasetTLV));
+ ReturnOnFailure(dataset.Init(datasetTLV));
+ // The Thread network is not enabled, but has a different extended pan id.
+ ReturnOnFailure(dataset.GetExtendedPanId(extpanid));
+ // If we don't have a valid dataset, we are not attempting to connect the network.
+
+ // We have already connected to the network, thus return success.
+ if (ThreadStackMgrImpl().IsThreadAttached())
+ {
+ mpStatusChangeCallback->OnNetworkingStatusChange(Status::kSuccess, MakeOptional(ByteSpan(extpanid)), NullOptional);
+ }
+ else
+ {
+ mpStatusChangeCallback->OnNetworkingStatusChange(Status::kNetworkNotFound, MakeOptional(ByteSpan(extpanid)),
+ MakeOptional(static_cast<int32_t>(OT_ERROR_DETACHED)));
+ }
+}
+
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT
static_assert(OPENTHREAD_API_VERSION >= 156, "SRP Client requires a more recent OpenThread version");
diff --git a/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.h b/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.h
index 5b44e32..7cd8dac 100644
--- a/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.h
+++ b/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.h
@@ -70,6 +70,11 @@
otInstance * OTInstance() const;
static void OnOpenThreadStateChange(uint32_t flags, void * context);
inline void OverrunErrorTally(void);
+ void
+ SetNetworkStatusChangeCallback(NetworkCommissioning::Internal::BaseDriver::NetworkStatusChangeCallback * statusChangeCallback)
+ {
+ mpStatusChangeCallback = statusChangeCallback;
+ }
protected:
// ===== Methods that implement the ThreadStackManager abstract interface.
@@ -92,6 +97,7 @@
CHIP_ERROR _StartThreadScan(NetworkCommissioning::ThreadDriver::ScanCallback * callback);
static void _OnNetworkScanFinished(otActiveScanResult * aResult, void * aContext);
void _OnNetworkScanFinished(otActiveScanResult * aResult);
+ void _UpdateNetworkStatus();
#if CHIP_DEVICE_CONFIG_ENABLE_SED
CHIP_ERROR _GetSEDPollingConfig(ConnectivityManager::SEDPollingConfig & pollingConfig);
@@ -148,6 +154,7 @@
NetworkCommissioning::ThreadDriver::ScanCallback * mpScanCallback;
NetworkCommissioning::Internal::WirelessDriver::ConnectCallback * mpConnectCallback;
+ NetworkCommissioning::Internal::BaseDriver::NetworkStatusChangeCallback * mpStatusChangeCallback = nullptr;
#if CHIP_DEVICE_CONFIG_ENABLE_SED
ConnectivityManager::SEDPollingConfig mPollingConfig;
diff --git a/src/platform/P6/NetworkCommissioningDriver.h b/src/platform/P6/NetworkCommissioningDriver.h
index a1c30b4..54302bd 100644
--- a/src/platform/P6/NetworkCommissioningDriver.h
+++ b/src/platform/P6/NetworkCommissioningDriver.h
@@ -91,7 +91,7 @@
// BaseDriver
NetworkIterator * GetNetworks() override { return new WiFiNetworkIterator(this); }
- CHIP_ERROR Init() override;
+ CHIP_ERROR Init(NetworkStatusChangeCallback * networkStatusChangeCallback) override;
CHIP_ERROR Shutdown() override;
// WirelessDriver
diff --git a/src/platform/P6/NetworkCommissioningWiFiDriver.cpp b/src/platform/P6/NetworkCommissioningWiFiDriver.cpp
index fe911c7..c4b2a82 100644
--- a/src/platform/P6/NetworkCommissioningWiFiDriver.cpp
+++ b/src/platform/P6/NetworkCommissioningWiFiDriver.cpp
@@ -39,7 +39,7 @@
cy_wcm_scan_result_t scan_result_list[kWiFiMaxNetworks];
uint8_t NumAP; // no of network scanned
-CHIP_ERROR P6WiFiDriver::Init()
+CHIP_ERROR P6WiFiDriver::Init(NetworkStatusChangeCallback * networkStatusChangeCallback)
{
CHIP_ERROR err;
size_t ssidLen = 0;
diff --git a/src/platform/Tizen/NetworkCommissioningDriver.h b/src/platform/Tizen/NetworkCommissioningDriver.h
index c7b23ca..5c00f8b 100644
--- a/src/platform/Tizen/NetworkCommissioningDriver.h
+++ b/src/platform/Tizen/NetworkCommissioningDriver.h
@@ -52,7 +52,7 @@
// BaseDriver
NetworkIterator * GetNetworks() override { return new WiFiNetworkIterator(this); }
- CHIP_ERROR Init() override;
+ CHIP_ERROR Init(BaseDriver::NetworkStatusChangeCallback * networkStatusChangeCallback) override;
CHIP_ERROR Shutdown() override { return CHIP_NO_ERROR; }
// WirelessDriver
diff --git a/src/platform/Tizen/NetworkCommissioningWiFiDriver.cpp b/src/platform/Tizen/NetworkCommissioningWiFiDriver.cpp
index 43a71c8..fa444f9 100644
--- a/src/platform/Tizen/NetworkCommissioningWiFiDriver.cpp
+++ b/src/platform/Tizen/NetworkCommissioningWiFiDriver.cpp
@@ -38,7 +38,7 @@
constexpr char kWiFiCredentialsKeyName[] = "wifi-pass";
} // namespace
-CHIP_ERROR TizenWiFiDriver::Init()
+CHIP_ERROR TizenWiFiDriver::Init(BaseDriver::NetworkStatusChangeCallback * networkStatusChangeCallback)
{
CHIP_ERROR err;
size_t ssidLen = 0;
diff --git a/src/platform/mbed/ConnectivityManagerImpl_WiFi.cpp b/src/platform/mbed/ConnectivityManagerImpl_WiFi.cpp
index 57aca8b..d5e0200 100644
--- a/src/platform/mbed/ConnectivityManagerImpl_WiFi.cpp
+++ b/src/platform/mbed/ConnectivityManagerImpl_WiFi.cpp
@@ -56,7 +56,7 @@
#if CHIP_DEVICE_ENABLE_DATA_MODEL
err = sWiFiNetworkCommissioningInstance.Init();
#else
- err = NetworkCommissioning::WiFiDriverImpl::GetInstance().Init();
+ err = NetworkCommissioning::WiFiDriverImpl::GetInstance().Init(nullptr);
#endif
VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(DeviceLayer, "WiFi driver init failed: %s", chip::ErrorStr(err)));
@@ -116,7 +116,7 @@
{
if (mWiFiStationMode == kWiFiStationMode_Enabled)
{
- NetworkCommissioning::WiFiDriverImpl::GetInstance().Init();
+ NetworkCommissioning::WiFiDriverImpl::GetInstance().Init(nullptr);
}
else if (mWiFiStationMode == kWiFiStationMode_Disabled)
{
diff --git a/src/platform/mbed/NetworkCommissioningDriver.h b/src/platform/mbed/NetworkCommissioningDriver.h
index 1a374f5..c742446 100644
--- a/src/platform/mbed/NetworkCommissioningDriver.h
+++ b/src/platform/mbed/NetworkCommissioningDriver.h
@@ -93,7 +93,7 @@
// BaseDriver
NetworkIterator * GetNetworks() override { return new WiFiNetworkIterator(this); }
- CHIP_ERROR Init() override;
+ CHIP_ERROR Init(NetworkStatusChangeCallback * networkStatusChangeCallback) override;
CHIP_ERROR Shutdown() override;
// WirelessDriver
diff --git a/src/platform/mbed/NetworkCommissioningWiFiDriver.cpp b/src/platform/mbed/NetworkCommissioningWiFiDriver.cpp
index f6918e6..d813b83 100644
--- a/src/platform/mbed/NetworkCommissioningWiFiDriver.cpp
+++ b/src/platform/mbed/NetworkCommissioningWiFiDriver.cpp
@@ -34,7 +34,7 @@
constexpr char kWiFiCredentialsKeyName[] = "wifi-pass";
} // namespace
-CHIP_ERROR WiFiDriverImpl::Init()
+CHIP_ERROR WiFiDriverImpl::Init(NetworkStatusChangeCallback * networkStatusChangeCallback)
{
size_t ssidLen = 0;
size_t credentialsLen = 0;