[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/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;
};