blob: 799790b28c351f01a741bedb5f65827852467a6b [file] [log] [blame]
/*
* WPA Supplicant - Supplicant Aidl interface
* Copyright (c) 2021, Google Inc. All rights reserved.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "aidl_manager.h"
#include "aidl_return_util.h"
#include "misc_utils.h"
#include "supplicant.h"
#include "p2p_iface.h"
#include <android-base/file.h>
#include <fcntl.h>
#include <sys/stat.h>
namespace {
// Pre-populated interface params for interfaces controlled by wpa_supplicant.
// Note: This may differ for other OEM's. So, modify this accordingly.
constexpr char kIfaceDriverName[] = "nl80211";
constexpr char kStaIfaceConfPath[] =
"/data/vendor/wifi/wpa/wpa_supplicant.conf";
static const char* kStaIfaceConfOverlayPaths[] = {
"/apex/com.android.wifi.hal/etc/wifi/wpa_supplicant_overlay.conf",
"/vendor/etc/wifi/wpa_supplicant_overlay.conf",
};
constexpr char kP2pIfaceConfPath[] =
"/data/vendor/wifi/wpa/p2p_supplicant.conf";
static const char* kP2pIfaceConfOverlayPaths[] = {
"/apex/com.android.wifi.hal/etc/wifi/p2p_supplicant_overlay.conf",
"/vendor/etc/wifi/p2p_supplicant_overlay.conf",
};
// Migrate conf files for existing devices.
static const char* kTemplateConfPaths[] = {
"/apex/com.android.wifi.hal/etc/wifi/wpa_supplicant.conf",
"/vendor/etc/wifi/wpa_supplicant.conf",
"/system/etc/wifi/wpa_supplicant.conf",
};
constexpr char kOldStaIfaceConfPath[] = "/data/misc/wifi/wpa_supplicant.conf";
constexpr char kOldP2pIfaceConfPath[] = "/data/misc/wifi/p2p_supplicant.conf";
constexpr mode_t kConfigFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
const char* resolvePath(const char* paths[], size_t size)
{
for (int i = 0; i < size; ++i) {
if (access(paths[i], R_OK) == 0) {
return paths[i];
}
}
return nullptr;
}
int copyFile(
const std::string& src_file_path, const std::string& dest_file_path)
{
std::string file_contents;
if (!android::base::ReadFileToString(src_file_path, &file_contents)) {
wpa_printf(
MSG_ERROR, "Failed to read from %s. Errno: %s",
src_file_path.c_str(), strerror(errno));
return -1;
}
if (!android::base::WriteStringToFile(
file_contents, dest_file_path, kConfigFileMode, getuid(),
getgid())) {
wpa_printf(
MSG_ERROR, "Failed to write to %s. Errno: %s",
dest_file_path.c_str(), strerror(errno));
return -1;
}
return 0;
}
/**
* Copy |src_file_path| to |dest_file_path| if it exists.
*
* Returns 1 if |src_file_path| does not exist or not accessible,
* Returns -1 if the copy fails.
* Returns 0 if the copy succeeds.
*/
int copyFileIfItExists(
const std::string& src_file_path, const std::string& dest_file_path)
{
int ret = access(src_file_path.c_str(), R_OK);
// Sepolicy denial (2018+ device) will return EACCESS instead of ENOENT.
if ((ret != 0) && ((errno == ENOENT) || (errno == EACCES))) {
return 1;
}
ret = copyFile(src_file_path, dest_file_path);
if (ret != 0) {
wpa_printf(
MSG_ERROR, "Failed copying %s to %s.",
src_file_path.c_str(), dest_file_path.c_str());
return -1;
}
return 0;
}
/**
* Ensure that the specified config file pointed by |config_file_path| exists.
* a) If the |config_file_path| exists with the correct permissions, return.
* b) If the |config_file_path| does not exist, but |old_config_file_path|
* exists, copy over the contents of the |old_config_file_path| to
* |config_file_path|.
* c) If the |config_file_path| & |old_config_file_path|
* does not exists, copy over the contents of |template_config_file_path|.
*/
int ensureConfigFileExists(
const std::string& config_file_path,
const std::string& old_config_file_path)
{
int ret = access(config_file_path.c_str(), R_OK | W_OK);
if (ret == 0) {
return 0;
}
if (errno == EACCES) {
ret = chmod(config_file_path.c_str(), kConfigFileMode);
if (ret == 0) {
return 0;
} else {
wpa_printf(
MSG_ERROR, "Cannot set RW to %s. Errno: %s",
config_file_path.c_str(), strerror(errno));
return -1;
}
} else if (errno != ENOENT) {
wpa_printf(
MSG_ERROR, "Cannot acces %s. Errno: %s",
config_file_path.c_str(), strerror(errno));
return -1;
}
ret = copyFileIfItExists(old_config_file_path, config_file_path);
if (ret == 0) {
wpa_printf(
MSG_INFO, "Migrated conf file from %s to %s",
old_config_file_path.c_str(), config_file_path.c_str());
unlink(old_config_file_path.c_str());
return 0;
} else if (ret == -1) {
unlink(config_file_path.c_str());
return -1;
}
const char* path =
resolvePath(kTemplateConfPaths,
sizeof(kTemplateConfPaths)/sizeof(kTemplateConfPaths[0]));
if (path != nullptr) {
ret = copyFileIfItExists(path, config_file_path);
if (ret == 0) {
wpa_printf(
MSG_INFO, "Copied template conf file from %s to %s",
path, config_file_path.c_str());
return 0;
} else if (ret == -1) {
unlink(config_file_path.c_str());
return -1;
}
}
// Did not create the conf file.
return -1;
}
} // namespace
namespace aidl {
namespace android {
namespace hardware {
namespace wifi {
namespace supplicant {
using aidl_return_util::validateAndCall;
using misc_utils::createStatus;
using misc_utils::createStatusWithMsg;
Supplicant::Supplicant(struct wpa_global* global) : wpa_global_(global) {}
bool Supplicant::isValid()
{
// This top level object cannot be invalidated.
return true;
}
::ndk::ScopedAStatus Supplicant::addP2pInterface(
const std::string& in_name,
std::shared_ptr<ISupplicantP2pIface>* _aidl_return)
{
return validateAndCall(
this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
&Supplicant::addP2pInterfaceInternal, _aidl_return, in_name);
}
::ndk::ScopedAStatus Supplicant::addStaInterface(
const std::string& in_name,
std::shared_ptr<ISupplicantStaIface>* _aidl_return)
{
return validateAndCall(
this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
&Supplicant::addStaInterfaceInternal, _aidl_return, in_name);
}
::ndk::ScopedAStatus Supplicant::removeInterface(
const IfaceInfo& in_ifaceInfo)
{
return validateAndCall(
this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
&Supplicant::removeInterfaceInternal, in_ifaceInfo);
}
::ndk::ScopedAStatus Supplicant::getP2pInterface(
const std::string& in_name,
std::shared_ptr<ISupplicantP2pIface>* _aidl_return)
{
return validateAndCall(
this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
&Supplicant::getP2pInterfaceInternal, _aidl_return, in_name);
}
::ndk::ScopedAStatus Supplicant::getStaInterface(
const std::string& in_name,
std::shared_ptr<ISupplicantStaIface>* _aidl_return)
{
return validateAndCall(
this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
&Supplicant::getStaInterfaceInternal, _aidl_return, in_name);
}
::ndk::ScopedAStatus Supplicant::listInterfaces(
std::vector<IfaceInfo>* _aidl_return)
{
return validateAndCall(
this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
&Supplicant::listInterfacesInternal, _aidl_return);
}
::ndk::ScopedAStatus Supplicant::registerCallback(
const std::shared_ptr<ISupplicantCallback>& in_callback)
{
return validateAndCall(
this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
&Supplicant::registerCallbackInternal, in_callback);
}
::ndk::ScopedAStatus Supplicant::setDebugParams(
DebugLevel in_level, bool in_showTimestamp,
bool in_showKeys)
{
return validateAndCall(
this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
&Supplicant::setDebugParamsInternal, in_level,
in_showTimestamp, in_showKeys);
}
::ndk::ScopedAStatus Supplicant::setConcurrencyPriority(
IfaceType in_type)
{
return validateAndCall(
this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
&Supplicant::setConcurrencyPriorityInternal, in_type);
}
::ndk::ScopedAStatus Supplicant::getDebugLevel(DebugLevel* _aidl_return)
{
*_aidl_return = static_cast<DebugLevel>(wpa_debug_level);
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus Supplicant::isDebugShowTimestampEnabled(bool* _aidl_return)
{
*_aidl_return = ((wpa_debug_timestamp != 0) ? true : false);
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus Supplicant::isDebugShowKeysEnabled(bool* _aidl_return)
{
*_aidl_return = ((wpa_debug_show_keys != 0) ? true : false);
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus Supplicant::terminate()
{
wpa_printf(MSG_INFO, "Terminating...");
wpa_supplicant_terminate_proc(wpa_global_);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Supplicant::addP2pDevInterface(struct wpa_interface iface_params)
{
char primary_ifname[IFNAMSIZ];
u32 primary_ifname_len =
strlen(iface_params.ifname) - strlen(P2P_MGMT_DEVICE_PREFIX);
if(primary_ifname_len > IFNAMSIZ) {
wpa_printf(MSG_DEBUG, "%s, Invalid primary iface name ", __FUNCTION__);
return createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID);
}
strncpy(primary_ifname, iface_params.ifname +
strlen(P2P_MGMT_DEVICE_PREFIX), primary_ifname_len);
wpa_printf(MSG_DEBUG, "%s, Initialize p2p-dev-wlan0 iface with"
"primary_iface = %s", __FUNCTION__, primary_ifname);
struct wpa_supplicant* wpa_s =
wpa_supplicant_get_iface(wpa_global_, primary_ifname);
if (!wpa_s) {
wpa_printf(MSG_DEBUG, "%s,NULL wpa_s for wlan0", __FUNCTION__);
return createStatus(SupplicantStatusCode::FAILURE_IFACE_UNKNOWN);
}
const u8 *if_addr = NULL;
char force_name[100] = {'\0'};
wpa_s->pending_interface_type = WPA_IF_P2P_DEVICE;
if (wpa_s->conf->p2p_device_random_mac_addr == 2 &&
!is_zero_ether_addr(wpa_s->conf->p2p_device_persistent_mac_addr))
if_addr = wpa_s->conf->p2p_device_persistent_mac_addr;
int ret = wpa_drv_if_add(wpa_s, WPA_IF_P2P_DEVICE, iface_params.ifname, if_addr, NULL,
force_name, wpa_s->pending_interface_addr, NULL);
if (ret < 0) {
wpa_printf(MSG_DEBUG, "P2P: Failed to create P2P Device interface");
return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
}
os_strlcpy(wpa_s->pending_interface_name, iface_params.ifname,
sizeof(wpa_s->pending_interface_name));
iface_params.p2p_mgmt = 1;
iface_params.driver_param = wpa_s->conf->driver_param;
iface_params.ctrl_interface = NULL;
struct wpa_supplicant *p2pdev_wpa_s = wpa_supplicant_add_iface(
wpa_s->global, &iface_params, wpa_s);
if (!p2pdev_wpa_s) {
wpa_printf(MSG_INFO,
"Failed to enable P2P Device");
return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
}
p2pdev_wpa_s->p2pdev = p2pdev_wpa_s;
wpa_s->pending_interface_name[0] = '\0';
return ndk::ScopedAStatus::ok();
}
std::pair<std::shared_ptr<ISupplicantP2pIface>, ndk::ScopedAStatus>
Supplicant::addP2pInterfaceInternal(const std::string& name)
{
std::shared_ptr<ISupplicantP2pIface> iface;
// Check if required |ifname| argument is empty.
if (name.empty()) {
return {nullptr, createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID)};
}
if (strncmp(name.c_str(), P2P_MGMT_DEVICE_PREFIX, strlen(P2P_MGMT_DEVICE_PREFIX)) == 0) {
struct wpa_supplicant* wpa_s = wpa_supplicant_get_iface(wpa_global_, name.c_str());
if (wpa_s) {
wpa_printf(MSG_DEBUG, "Remove existing p2p dev interface");
wpa_supplicant_remove_iface(wpa_global_, wpa_s, 0);
}
}
// Try to get the wpa_supplicant record for this iface, return
// the iface object with the appropriate status code if it exists.
ndk::ScopedAStatus status;
std::tie(iface, status) = getP2pInterfaceInternal(name);
if (status.isOk()) {
wpa_printf(MSG_INFO, "Iface already exists, return existing");
return {iface, ndk::ScopedAStatus::ok()};
}
struct wpa_interface iface_params = {};
iface_params.driver = kIfaceDriverName;
if (ensureConfigFileExists(
kP2pIfaceConfPath, kOldP2pIfaceConfPath) != 0) {
wpa_printf(
MSG_ERROR, "Conf file does not exists: %s",
kP2pIfaceConfPath);
return {nullptr, createStatusWithMsg(
SupplicantStatusCode::FAILURE_UNKNOWN, "Conf file does not exist")};
}
iface_params.confname = kP2pIfaceConfPath;
const char* path = resolvePath(
kP2pIfaceConfOverlayPaths,
sizeof(kP2pIfaceConfOverlayPaths)/sizeof(kP2pIfaceConfOverlayPaths[0]));
if (path != nullptr) {
iface_params.confanother = path;
}
iface_params.ifname = name.c_str();
if (strncmp(iface_params.ifname, P2P_MGMT_DEVICE_PREFIX,
strlen(P2P_MGMT_DEVICE_PREFIX)) == 0) {
status = addP2pDevInterface(iface_params);
if (!status.isOk()) {
return {iface, createStatus(static_cast<SupplicantStatusCode>(
status.getServiceSpecificError()))};
}
} else {
struct wpa_supplicant* wpa_s =
wpa_supplicant_add_iface(wpa_global_, &iface_params, NULL);
if (!wpa_s) {
return {nullptr, createStatus(SupplicantStatusCode::FAILURE_UNKNOWN)};
}
// Request the current scan results from the driver and update
// the local BSS list wpa_s->bss. This is to avoid a full scan
// while processing the connect request on newly created interface.
wpa_supplicant_update_scan_results(wpa_s);
}
// The supplicant core creates a corresponding aidl object via
// AidlManager when |wpa_supplicant_add_iface| is called.
return getP2pInterfaceInternal(name);
}
std::pair<std::shared_ptr<ISupplicantStaIface>, ndk::ScopedAStatus>
Supplicant::addStaInterfaceInternal(const std::string& name)
{
std::shared_ptr<ISupplicantStaIface> iface;
// Check if required |ifname| argument is empty.
if (name.empty()) {
return {nullptr, createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID)};
}
// Try to get the wpa_supplicant record for this iface, return
// the iface object with the appropriate status code if it exists.
ndk::ScopedAStatus status;
std::tie(iface, status) = getStaInterfaceInternal(name);
if (status.isOk()) {
wpa_printf(MSG_INFO, "Iface already exists, return existing");
return {iface, ndk::ScopedAStatus::ok()};
}
struct wpa_interface iface_params = {};
iface_params.driver = kIfaceDriverName;
if (ensureConfigFileExists(
kStaIfaceConfPath, kOldStaIfaceConfPath) != 0) {
wpa_printf(
MSG_ERROR, "Conf file does not exists: %s",
kStaIfaceConfPath);
return {nullptr, createStatusWithMsg(
SupplicantStatusCode::FAILURE_UNKNOWN, "Conf file does not exist")};
}
iface_params.confname = kStaIfaceConfPath;
const char* path = resolvePath(
kStaIfaceConfOverlayPaths,
sizeof(kStaIfaceConfOverlayPaths)/sizeof(kStaIfaceConfOverlayPaths[0]));
if (path != nullptr) {
iface_params.confanother = path;
}
iface_params.ifname = name.c_str();
if (strncmp(iface_params.ifname, P2P_MGMT_DEVICE_PREFIX,
strlen(P2P_MGMT_DEVICE_PREFIX)) == 0) {
status = addP2pDevInterface(iface_params);
if (!status.isOk()) {
return {iface, createStatus(static_cast<SupplicantStatusCode>(
status.getServiceSpecificError()))};
}
} else {
struct wpa_supplicant* wpa_s =
wpa_supplicant_add_iface(wpa_global_, &iface_params, NULL);
if (!wpa_s) {
return {nullptr, createStatus(SupplicantStatusCode::FAILURE_UNKNOWN)};
}
// Request the current scan results from the driver and update
// the local BSS list wpa_s->bss. This is to avoid a full scan
// while processing the connect request on newly created interface.
wpa_supplicant_update_scan_results(wpa_s);
}
// The supplicant core creates a corresponding aidl object via
// AidlManager when |wpa_supplicant_add_iface| is called.
return getStaInterfaceInternal(name);
}
ndk::ScopedAStatus Supplicant::removeInterfaceInternal(
const IfaceInfo& iface_info)
{
struct wpa_supplicant* wpa_s =
wpa_supplicant_get_iface(wpa_global_, iface_info.name.c_str());
if (!wpa_s) {
return createStatus(SupplicantStatusCode::FAILURE_IFACE_UNKNOWN);
}
if (wpa_supplicant_remove_iface(wpa_global_, wpa_s, 0)) {
return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
}
return ndk::ScopedAStatus::ok();
}
std::pair<std::shared_ptr<ISupplicantP2pIface>, ndk::ScopedAStatus>
Supplicant::getP2pInterfaceInternal(const std::string& name)
{
struct wpa_supplicant* wpa_s =
wpa_supplicant_get_iface(wpa_global_, name.c_str());
if (!wpa_s) {
return {nullptr, createStatus(SupplicantStatusCode::FAILURE_IFACE_UNKNOWN)};
}
AidlManager* aidl_manager = AidlManager::getInstance();
std::shared_ptr<ISupplicantP2pIface> iface;
if (!aidl_manager ||
aidl_manager->getP2pIfaceAidlObjectByIfname(
wpa_s->ifname, &iface)) {
return {iface, createStatus(SupplicantStatusCode::FAILURE_UNKNOWN)};
}
// Set this flag true here, since there is no AIDL initialize
// method for the p2p config, and the supplicant interface is
// not ready when the p2p iface is created.
wpa_s->conf->persistent_reconnect = true;
return {iface, ndk::ScopedAStatus::ok()};
}
std::pair<std::shared_ptr<ISupplicantStaIface>, ndk::ScopedAStatus>
Supplicant::getStaInterfaceInternal(const std::string& name)
{
struct wpa_supplicant* wpa_s =
wpa_supplicant_get_iface(wpa_global_, name.c_str());
if (!wpa_s) {
return {nullptr, createStatus(SupplicantStatusCode::FAILURE_IFACE_UNKNOWN)};
}
AidlManager* aidl_manager = AidlManager::getInstance();
std::shared_ptr<ISupplicantStaIface> iface;
if (!aidl_manager ||
aidl_manager->getStaIfaceAidlObjectByIfname(
wpa_s->ifname, &iface)) {
return {iface, createStatus(SupplicantStatusCode::FAILURE_UNKNOWN)};
}
return {iface, ndk::ScopedAStatus::ok()};
}
std::pair<std::vector<IfaceInfo>, ndk::ScopedAStatus>
Supplicant::listInterfacesInternal()
{
std::vector<IfaceInfo> ifaces;
for (struct wpa_supplicant* wpa_s = wpa_global_->ifaces; wpa_s;
wpa_s = wpa_s->next) {
if (wpa_s->global->p2p_init_wpa_s == wpa_s) {
ifaces.emplace_back(IfaceInfo{
IfaceType::P2P, wpa_s->ifname});
} else {
ifaces.emplace_back(IfaceInfo{
IfaceType::STA, wpa_s->ifname});
}
}
return {std::move(ifaces), ndk::ScopedAStatus::ok()};
}
ndk::ScopedAStatus Supplicant::registerCallbackInternal(
const std::shared_ptr<ISupplicantCallback>& callback)
{
AidlManager* aidl_manager = AidlManager::getInstance();
if (!aidl_manager ||
aidl_manager->addSupplicantCallbackAidlObject(callback)) {
return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Supplicant::setDebugParamsInternal(
DebugLevel level, bool show_timestamp, bool show_keys)
{
if (wpa_supplicant_set_debug_params(
wpa_global_, static_cast<uint32_t>(level), show_timestamp,
show_keys)) {
return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Supplicant::setConcurrencyPriorityInternal(IfaceType type)
{
if (type == IfaceType::STA) {
wpa_global_->conc_pref =
wpa_global::wpa_conc_pref::WPA_CONC_PREF_STA;
} else if (type == IfaceType::P2P) {
wpa_global_->conc_pref =
wpa_global::wpa_conc_pref::WPA_CONC_PREF_P2P;
} else {
return createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID);
}
return ndk::ScopedAStatus::ok();
}
} // namespace supplicant
} // namespace wifi
} // namespace hardware
} // namespace android
} // namespace aidl