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