blob: efe2d146f4f5e441c8da8f5d8de194585510e41e [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.
*/
#include "WpaSupplicantClient.h"
#include <mutex>
#include <lib/support/CHIPMemString.h>
#include <platform/CHIPDeviceConfig.h>
namespace chip {
namespace DeviceLayer {
namespace Internal {
namespace {
// MARK: Global Variables
static constexpr char kWpaSupplicantServiceName[] = "fi.w1.wpa_supplicant1";
} // namespace
void WpaSupplicantClient::GDBusWpaSupplicant::Reset()
{
iface.reset();
proxy.reset();
interfacePath.reset();
networkPath.reset();
}
CHIP_ERROR WpaSupplicantClient::Init(ConnectivityManagerImpl & inConnectivityManagerImpl) noexcept
{
// Initially, and again at some future point, this should have an
// assertion that mConnectivityManagerImpl is null and otherwise
// return CHIP_ERROR_ALREADY_INITIALIZED to indicate to the caller
// that 'Init' has been double-tripped without a bookending call
// to Shutdown. However, the ConnectivityManager stack of which
// this is a part has no peer 'Shutdown' method to go with its
// 'Init' and subsequently, there is not yet a reasonable place to
// hook the 'Shutdown' for this class.
//
// At some point, revisit the top-down Init/Shutdown in
// ConnectivityManager. When that happens, plumb the call
// propagation through to this client 'Shutdown' and re-add the
// assertion.
mWiFiIfName[0] = '\0';
mConnectivityManagerImpl = &inConnectivityManagerImpl;
return CHIP_NO_ERROR;
}
void WpaSupplicantClient::Shutdown() noexcept
{
Reset();
mConnectivityManagerImpl = nullptr;
}
void WpaSupplicantClient::Reset() noexcept
{
std::lock_guard<std::mutex> lock(mWpaSupplicantMutex);
mWpaSupplicant.Reset();
}
bool WpaSupplicantClient::IsStarted() const noexcept
{
std::lock_guard<std::mutex> lock(mWpaSupplicantMutex);
return !!mWpaSupplicant.iface;
}
bool WpaSupplicantClient::IsWiFiInterfaceEnabled() const noexcept
{
VerifyOrReturnValue(mWpaSupplicant.iface, false);
// Check if the interface is not disabled (for example, due to rfkill or some other reasons).
return g_strcmp0(wpa_supplicant_1_interface_get_state(mWpaSupplicant.iface.get()), "interface_disabled") != 0;
}
CHIP_ERROR WpaSupplicantClient::GetConfiguredNetwork(NetworkCommissioning::Network & outNetwork) noexcept
{
// This function can be called without g_main_context_get_thread_default() being set.
// The network proxy object is created in a synchronous manner, so the D-Bus call will
// be completed before this function returns. Also, no external callbacks are registered
// with the proxy object.
std::lock_guard<std::mutex> lock(mWpaSupplicantMutex);
GAutoPtr<GError> err;
if (!mWpaSupplicant.iface)
{
ChipLogDetail(DeviceLayer, "Wifi network not currently connected");
return CHIP_ERROR_INCORRECT_STATE;
}
const char * networkPath = wpa_supplicant_1_interface_get_current_network(mWpaSupplicant.iface.get());
// wpa_supplicant DBus API: if network path of current network is "/", means no networks is currently selected.
if ((networkPath == nullptr) || (strcmp(networkPath, "/") == 0))
{
return CHIP_ERROR_KEY_NOT_FOUND;
}
GAutoPtr<WpaSupplicant1Network> networkInfo(wpa_supplicant_1_network_proxy_new_for_bus_sync(
G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, kWpaSupplicantServiceName, networkPath, nullptr, &err.GetReceiver()));
VerifyOrReturnError(
networkInfo, CHIP_ERROR_INTERNAL,
ChipLogError(DeviceLayer, WPA_SUPPLICANT_CLIENT_LOG_PREFIX "Failed to create network proxy: %s", err->message));
outNetwork.connected = wpa_supplicant_1_network_get_enabled(networkInfo.get());
GVariant * properties = wpa_supplicant_1_network_get_properties(networkInfo.get());
VerifyOrReturnError(properties != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
GAutoPtr<GVariant> ssid(g_variant_lookup_value(properties, "ssid", nullptr));
gsize length;
const gchar * ssidStr = g_variant_get_string(ssid.get(), &length);
// TODO: wpa_supplicant will return ssid with quotes! We should have a better way to get the actual ssid in bytes.
gsize length_actual = length - 2;
VerifyOrReturnError(length_actual <= sizeof(outNetwork.networkID), CHIP_ERROR_INTERNAL);
ChipLogDetail(DeviceLayer, "Current connected network: %s", StringOrNullMarker(ssidStr));
memcpy(outNetwork.networkID, ssidStr + 1, length_actual);
outNetwork.networkIDLen = length_actual;
return CHIP_NO_ERROR;
}
CHIP_ERROR WpaSupplicantClient::GetIfName(CharSpan & outIfName) const noexcept
{
outIfName = CharSpan::fromCharString(mWiFiIfName);
return CHIP_NO_ERROR;
}
CHIP_ERROR WpaSupplicantClient::SetIfName(const CharSpan & inIfName) noexcept
{
VerifyOrReturnError(inIfName.size() < std::size(mWiFiIfName), CHIP_ERROR_BUFFER_TOO_SMALL);
Platform::CopyString(mWiFiIfName, inIfName);
return CHIP_NO_ERROR;
}
} // namespace Internal
} // namespace DeviceLayer
} // namespace chip