blob: eb80e36a8622d73e10d97a5b6cbe3bb21f5f5303 [file]
/*
*
* Copyright (c) 2026 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <mutex>
#include <glib.h>
#include <inet/InetInterface.h>
#include <lib/support/FixedBuffer.h>
#include <lib/support/Span.h>
#include <platform/CHIPDeviceConfig.h>
#include <platform/CHIPDeviceEvent.h>
#include <platform/GLibTypeDeleter.h>
#include <platform/Linux/dbus/wpa/DBusWpa.h>
#include <platform/Linux/dbus/wpa/DBusWpaBss.h>
#include <platform/Linux/dbus/wpa/DBusWpaInterface.h>
#include <platform/Linux/dbus/wpa/DBusWpaNetwork.h>
#include <platform/NetworkCommissioning.h>
#include <system/SystemMutex.h>
#define WPA_SUPPLICANT_CLIENT_LOG_PREFIX "wpa_supplicant: "
namespace chip {
template <>
struct GAutoPtrDeleter<WpaSupplicant1>
{
using deleter = GObjectDeleter;
};
template <>
struct GAutoPtrDeleter<WpaSupplicant1BSS>
{
using deleter = GObjectDeleter;
};
template <>
struct GAutoPtrDeleter<WpaSupplicant1Interface>
{
using deleter = GObjectDeleter;
};
template <>
struct GAutoPtrDeleter<WpaSupplicant1Network>
{
using deleter = GObjectDeleter;
};
namespace DeviceLayer {
// Forward Declarations
class ConnectivityManagerImpl;
namespace Internal {
/**
* @brief
* A D-Bus client for the wpa_supplicant Wi-Fi control plane.
*
* Encapsulates the GLib/D-Bus proxy objects and shared state
* needed to interact with the wpa_supplicant daemon over the
* D-Bus system bus.
*
* This class is intended as a base for concrete Wi-Fi feature
* implementations (for example, network management or Wi-Fi USD NAN
* / PAF) that share a common wpa_supplicant back end.
*
* Subclasses inherit the D-Bus proxy state (#mWpaSupplicant) and its
* associated mutex (#mWpaSupplicantMutex), and must call @c Init
* before using any wpa_supplicant interactions.
*
* The expected lifecycle is:
*
* 1. Construct (default, via subclass).
* 2. Initinitalization (via #Init).
* a. Binds to a ConnectivityManagerImpl for event dispatch.
* 3. Use.
* 4. Shutdown (via #Shutdown)
* a. Tears down proxy state and releases the
* ConnectivityManagerImpl back-reference.
*
*/
class WpaSupplicantClient
{
public:
virtual ~WpaSupplicantClient() noexcept = default;
protected:
WpaSupplicantClient() noexcept = default;
/**
* @brief
* Bind to a Connectivity Manager and prepare for D-Bus
* interactions with wpa_supplicant.
*
* Associates this client with the given platform Connectivity
* Manager implementation, which serves as the target for
* asynchronous event and status callbacks originating from
* wpa_supplicant D-Bus signals (for example, scan results,
* connection state changes).
*
* @param[in] inConnectivityManagerImpl
* A reference to the platform Connectivity Manager
* implementation to which wpa_supplicant events will be
* dispatched.
*
* @sa Shutdown
* @sa Reset
*
*/
CHIP_ERROR Init(ConnectivityManagerImpl & inConnectivityManagerImpl) noexcept;
/**
* @brief
* Tear down D-Bus proxy state and release the Connectivity
* Manager back-reference.
*
* Resets all wpa_supplicant D-Bus proxy objects via Reset and
* then clears the ConnectivityManagerImpl association
* established by #Init, returning this client to an uninitialized
* state suitable for re-initialization or destruction.
*
* @sa Init
* @sa Reset
*
*/
void Shutdown() noexcept;
/**
* @brief
* Reset the D-Bus proxy objects to their default state.
*
* Releases all GLib D-Bus proxy resources held in
* #mWpaSupplicant, including the wpa_supplicant daemon proxy,
* interface proxy, and any cached interface or network object
* paths. The ConnectivityManagerImpl back-reference is @b not
* cleared; use Shutdown for a full teardown.
*
* @sa Init
* @sa Shutdown
*
*/
void Reset() noexcept;
/**
* @brief
* Return whether the wpa_supplicant client has been started.
*
*/
bool IsStarted() const noexcept;
/**
* @brief
* Return whether the Wi-Fi network interface is enabled.
*
* @return
* @c True if the Wi-Fi network interface is enabled;
* otherwise, @c false.
*
*/
bool IsWiFiInterfaceEnabled() const noexcept CHIP_REQUIRES(mWpaSupplicantMutex);
/**
* @brief
* Get the Wi-Fi station service set identifier (SSID) and
* connected state.
*
* This attempts to get, if any, the currently connected (that
* is, associated) Wi-Fi station service set identifier (SSID)
* and connected state.
*
* @param[out] outNetwork
* A reference to the mutable network commissioning state to
* which to copy the currently connected Wi-Fi station SSID and
* connected state.
*
* @sa IsWiFiInterfaceEnabled
*
*/
CHIP_ERROR GetConfiguredNetwork(NetworkCommissioning::Network & outNetwork) noexcept;
/**
* @brief
* Get the Wi-Fi network interface name.
*
* @param[out] outIfName
* A reference to the mutable character span to which to assign
* the Wi-Fi network interface name extent.
*
* @sa SetIfName
*
*/
CHIP_ERROR GetIfName(CharSpan & outIfName) const noexcept;
/**
* @brief
* Set the Wi-Fi network interface name.
*
* @param[in] inIfName
* A reference to the immutable character span from which to
* copy the Wi-Fi network interface name.
*
* @retval CHIP_NO_ERROR
* If successful.
*
* @retval CHIP_ERROR_BUFFER_TOO_SMALL
* If the length of @a inIfName exceeds the internal buffer
* space.
*
* @sa GetIfName
*
*/
CHIP_ERROR SetIfName(const CharSpan & inIfName) noexcept;
void NotifyWiFiConnectivityChange(const ConnectivityChange & inChange);
CHIP_ERROR ScanNetwork(const ByteSpan & ssid);
void Start();
CHIP_ERROR StartSync();
CHIP_ERROR StopAutoScan();
virtual void UpdateWiFiNetworkStatus(){};
private:
bool GetBssInfo(const gchar * inBssPath, NetworkCommissioning::WiFiScanResponse & outWiFiScanResponse) const noexcept;
virtual void PostNetworkConnect(){};
virtual void OnWiFiMediumAvailable(WpaSupplicantClient & inOutWpaSupplicantClient, bool inAvailable){};
void OnWpaPropertiesChanged(WpaSupplicant1Interface * iface, GVariant * properties);
void OnWpaInterfaceProxyReady(GObject * sourceObject, GAsyncResult * res);
void OnWpaProxyReady(GObject * sourceObject, GAsyncResult * res);
void OnWpaInterfaceRemoved(WpaSupplicant1 * proxy, const char * path);
void OnWpaInterfaceAdded(WpaSupplicant1 * proxy, const char * path, GVariant * properties);
void OnWpaInterfaceScanDone(WpaSupplicant1Interface * iface, gboolean success);
void OnWpaInterfaceReady(GObject * sourceObject, GAsyncResult * res);
CHIP_ERROR StartOnGLib();
protected:
struct GDBusWpaSupplicant
{
GAutoPtr<WpaSupplicant1> proxy;
GAutoPtr<WpaSupplicant1Interface> iface;
GCharPtr interfacePath;
GCharPtr networkPath;
// Must be called synchronously on the GLib thread while the
// GLib main loop is still running.
void Reset();
};
GDBusWpaSupplicant mWpaSupplicant CHIP_GUARDED_BY(mWpaSupplicantMutex);
// Access to mWpaSupplicant has to be protected by a mutex because
// it is accessed from the CHIP event loop thread and dedicated
// GLib D-Bus thread started by platform manager.
mutable std::mutex mWpaSupplicantMutex;
private:
ConnectivityManagerImpl * mConnectivityManagerImpl = nullptr;
bool mAssociationStarted;
unsigned int mAssociationRetriesLeft;
char mWiFiIfName[Inet::InterfaceId::kMaxIfNameLength];
Internal::WiFiSSIDFixedBuffer mInterestedSSID;
};
} // namespace Internal
} // namespace DeviceLayer
} // namespace chip