blob: ce12386dfddb74d23a425efa088253e3b4aaf337 [file] [log] [blame] [edit]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "shill/wifi/hotspot_device.h"
#include <memory>
#include <utility>
#include <base/containers/contains.h>
#include <base/functional/bind.h>
#include "shill/control_interface.h"
#include "shill/device.h"
#include "shill/event_dispatcher.h"
#include "shill/supplicant/supplicant_interface_proxy_interface.h"
#include "shill/supplicant/supplicant_process_proxy_interface.h"
#include "shill/supplicant/wpa_supplicant.h"
#include "shill/wifi/hotspot_service.h"
#include "shill/wifi/local_service.h"
namespace shill {
namespace {
const char kInterfaceStateUnknown[] = "unknown";
} // namespace
// Constructor function
HotspotDevice::HotspotDevice(Manager* manager,
const std::string& primary_link_name,
const std::string& link_name,
const std::string& mac_address,
uint32_t phy_index,
LocalDevice::EventCallback callback)
: LocalDevice(
manager, IfaceType::kAP, link_name, mac_address, phy_index, callback),
primary_link_name_(primary_link_name),
prev_primary_iface_control_state_(false),
service_(nullptr),
supplicant_state_(kInterfaceStateUnknown) {
supplicant_primary_interface_path_ = RpcIdentifier("");
supplicant_interface_proxy_.reset();
supplicant_interface_path_ = RpcIdentifier("");
supplicant_network_path_ = RpcIdentifier("");
}
HotspotDevice::~HotspotDevice() {}
bool HotspotDevice::Start() {
prev_primary_iface_control_state_ = SupplicantProcessProxy()->GetInterface(
primary_link_name_, &supplicant_primary_interface_path_);
if (!prev_primary_iface_control_state_) {
// Connect wpa_supplicant to the primary interface.
KeyValueStore create_interface_args;
create_interface_args.Set<std::string>(
WPASupplicant::kInterfacePropertyName, primary_link_name_);
create_interface_args.Set<std::string>(
WPASupplicant::kInterfacePropertyDriver, WPASupplicant::kDriverNL80211);
create_interface_args.Set<std::string>(
WPASupplicant::kInterfacePropertyConfigFile,
WPASupplicant::kSupplicantConfPath);
if (!SupplicantProcessProxy()->CreateInterface(
create_interface_args, &supplicant_primary_interface_path_)) {
LOG(ERROR) << __func__ << ": Cannot connect to the primary interface "
<< primary_link_name_;
return false;
}
}
return CreateInterface();
}
bool HotspotDevice::Stop() {
bool ret = RemoveInterface();
if (!prev_primary_iface_control_state_ &&
!supplicant_primary_interface_path_.value().empty()) {
// Disconnect wpa_supplicant from the primary interface.
if (!SupplicantProcessProxy()->RemoveInterface(
supplicant_primary_interface_path_)) {
ret = false;
}
}
supplicant_primary_interface_path_ = RpcIdentifier("");
return ret;
}
bool HotspotDevice::ConfigureService(std::unique_ptr<HotspotService> service) {
CHECK(service);
if (service_) {
// Device has service configured.
LOG(ERROR) << __func__
<< ": Configure service to device which already has a service "
"configured.";
return false;
}
KeyValueStore service_params =
service->GetSupplicantConfigurationParameters();
if (!supplicant_interface_proxy_->AddNetwork(service_params,
&supplicant_network_path_)) {
LOG(ERROR) << __func__ << ": Failed to add network.";
return false;
}
CHECK(!supplicant_network_path_.value().empty());
service_ = std::move(service);
service_->SetState(LocalService::LocalServiceState::kStateStarting);
supplicant_interface_proxy_->SelectNetwork(supplicant_network_path_);
return true;
}
bool HotspotDevice::DeconfigureService() {
bool ret = true;
if (!supplicant_network_path_.value().empty() &&
!supplicant_interface_proxy_->RemoveNetwork(supplicant_network_path_)) {
ret = false;
}
supplicant_network_path_ = RpcIdentifier("");
if (service_) {
service_->SetState(LocalService::LocalServiceState::kStateIdle);
service_ = nullptr;
}
return ret;
}
bool HotspotDevice::CreateInterface() {
if (supplicant_interface_proxy_) {
return true;
}
KeyValueStore create_interface_args;
create_interface_args.Set<std::string>(WPASupplicant::kInterfacePropertyName,
link_name());
create_interface_args.Set<std::string>(
WPASupplicant::kInterfacePropertyDriver, WPASupplicant::kDriverNL80211);
create_interface_args.Set<std::string>(
WPASupplicant::kInterfacePropertyConfigFile,
WPASupplicant::kSupplicantConfPath);
create_interface_args.Set<bool>(WPASupplicant::kInterfacePropertyCreate,
true);
create_interface_args.Set<std::string>(
WPASupplicant::kInterfacePropertyType,
WPASupplicant::kInterfacePropertyTypeAP);
if (!mac_address().empty()) {
create_interface_args.Set<std::string>(
WPASupplicant::kInterfacePropertyAddress, mac_address());
}
if (!SupplicantProcessProxy()->CreateInterface(create_interface_args,
&supplicant_interface_path_)) {
// Interface might've already been created, attempt to retrieve it.
if (!SupplicantProcessProxy()->GetInterface(link_name(),
&supplicant_interface_path_)) {
LOG(ERROR) << __func__ << ": Failed to create interface with supplicant.";
return false;
}
}
supplicant_interface_proxy_ =
ControlInterface()->CreateSupplicantInterfaceProxy(
this, supplicant_interface_path_);
return true;
}
bool HotspotDevice::RemoveInterface() {
bool ret = true;
supplicant_interface_proxy_.reset();
if (!supplicant_interface_path_.value().empty() &&
!SupplicantProcessProxy()->RemoveInterface(supplicant_interface_path_)) {
ret = false;
}
supplicant_interface_path_ = RpcIdentifier("");
return ret;
}
void HotspotDevice::StateChanged(const std::string& new_state) {
if (supplicant_state_ == new_state)
return;
LOG(INFO) << "Interface " << link_name() << " state changed from "
<< supplicant_state_ << " to " << new_state;
// Convert state change to corresponding device event.
if (new_state == WPASupplicant::kInterfaceStateInterfaceDisabled) {
PostDeviceEvent(DeviceEvent::kInterfaceDisabled);
} else if (service_ && new_state == WPASupplicant::kInterfaceStateCompleted) {
service_->SetState(LocalService::LocalServiceState::kStateUp);
} else if (IsServiceUp() &&
(new_state == WPASupplicant::kInterfaceStateDisconnected ||
new_state == WPASupplicant::kInterfaceStateInactive)) {
DeconfigureService();
}
supplicant_state_ = new_state;
}
void HotspotDevice::PropertiesChangedTask(const KeyValueStore& properties) {
if (properties.Contains<std::string>(
WPASupplicant::kInterfacePropertyState)) {
StateChanged(
properties.Get<std::string>(WPASupplicant::kInterfacePropertyState));
}
// TODO(b/235762161): Add Stations property changed event handler to emit
// kPeerConnected and kPeerDisconnected device events.
}
// wpa_supplicant dbus event handlers for SupplicantEventDelegateInterface
void HotspotDevice::PropertiesChanged(const KeyValueStore& properties) {
Dispatcher()->PostTask(
FROM_HERE, base::BindOnce(&HotspotDevice::PropertiesChangedTask,
weak_ptr_factory_.GetWeakPtr(), properties));
}
void HotspotDevice::StationAdded(const RpcIdentifier& path,
const KeyValueStore& properties) {
if (base::Contains(stations_, path)) {
LOG(INFO) << "Receive StationAdded event for " << path.value()
<< ", which is already in the list. Ignore.";
return;
}
stations_[path] = properties;
auto aid = properties.Contains<uint16_t>(WPASupplicant::kStationPropertyAID)
? properties.Get<uint16_t>(WPASupplicant::kStationPropertyAID)
: -1;
LOG(INFO) << "Station [" << aid << "] connected to hotspot device "
<< link_name() << ", total station count: " << stations_.size();
PostDeviceEvent(DeviceEvent::kPeerConnected);
}
void HotspotDevice::StationRemoved(const RpcIdentifier& path) {
if (!base::Contains(stations_, path)) {
LOG(INFO) << "Receive StationRemoved event for " << path.value()
<< ", which is not in the list. Ignore.";
return;
}
auto aid =
stations_[path].Contains<uint16_t>(WPASupplicant::kStationPropertyAID)
? stations_[path].Get<uint16_t>(WPASupplicant::kStationPropertyAID)
: -1;
stations_.erase(path);
LOG(INFO) << "Station [" << aid << "] disconnected from hotspot device "
<< link_name() << ", total station count: " << stations_.size();
PostDeviceEvent(DeviceEvent::kPeerDisconnected);
}
std::vector<std::vector<uint8_t>> HotspotDevice::GetStations() {
std::vector<std::vector<uint8_t>> stations;
for (auto const& iter : stations_) {
std::vector<uint8_t> station;
if (iter.second.Contains<std::vector<uint8_t>>(
WPASupplicant::kStationPropertyAddress)) {
station = iter.second.Get<std::vector<uint8_t>>(
WPASupplicant::kStationPropertyAddress);
}
stations.push_back(station);
}
return stations;
}
} // namespace shill