blob: 9342ace103b047accc1ece77716da6fbdfaf09cd [file] [log] [blame]
/*
* hidl interface for wpa_supplicant daemon
* Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
* Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "hidl_manager.h"
#include "hidl_return_util.h"
#include "supplicant.h"
#include <android-base/file.h>
#include <fcntl.h>
#include <sys/stat.h>
namespace {
using namespace android::hardware::wifi::supplicant::V1_2;
// 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";
constexpr char kStaIfaceConfOverlayPath[] =
"/vendor/etc/wifi/wpa_supplicant_overlay.conf";
constexpr char kP2pIfaceConfPath[] =
"/data/vendor/wifi/wpa/p2p_supplicant.conf";
constexpr char kP2pIfaceConfOverlayPath[] =
"/vendor/etc/wifi/p2p_supplicant_overlay.conf";
// Migrate conf files for existing devices.
constexpr char kSystemTemplateConfPath[] =
"/system/etc/wifi/wpa_supplicant.conf";
constexpr char kVendorTemplateConfPath[] =
"/vendor/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;
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;
}
ret = copyFileIfItExists(kVendorTemplateConfPath, config_file_path);
if (ret == 0) {
wpa_printf(
MSG_INFO, "Copied template conf file from %s to %s",
kVendorTemplateConfPath, config_file_path.c_str());
return 0;
} else if (ret == -1) {
unlink(config_file_path.c_str());
return -1;
}
ret = copyFileIfItExists(kSystemTemplateConfPath, config_file_path);
if (ret == 0) {
wpa_printf(
MSG_INFO, "Copied template conf file from %s to %s",
kSystemTemplateConfPath, 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 android {
namespace hardware {
namespace wifi {
namespace supplicant {
namespace V1_2 {
namespace implementation {
using hidl_return_util::validateAndCall;
Supplicant::Supplicant(struct wpa_global* global) : wpa_global_(global) {}
bool Supplicant::isValid()
{
// This top level object cannot be invalidated.
return true;
}
Return<void> Supplicant::addInterface(
const IfaceInfo& iface_info, addInterface_cb _hidl_cb)
{
return validateAndCall(
this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
&Supplicant::addInterfaceInternal, _hidl_cb, iface_info);
}
Return<void> Supplicant::removeInterface(
const IfaceInfo& iface_info, removeInterface_cb _hidl_cb)
{
return validateAndCall(
this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
&Supplicant::removeInterfaceInternal, _hidl_cb, iface_info);
}
Return<void> Supplicant::getInterface(
const IfaceInfo& iface_info, getInterface_cb _hidl_cb)
{
return validateAndCall(
this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
&Supplicant::getInterfaceInternal, _hidl_cb, iface_info);
}
Return<void> Supplicant::listInterfaces(listInterfaces_cb _hidl_cb)
{
return validateAndCall(
this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
&Supplicant::listInterfacesInternal, _hidl_cb);
}
Return<void> Supplicant::registerCallback(
const sp<ISupplicantCallback>& callback, registerCallback_cb _hidl_cb)
{
return validateAndCall(
this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
&Supplicant::registerCallbackInternal, _hidl_cb, callback);
}
Return<void> Supplicant::setDebugParams(
ISupplicant::DebugLevel level, bool show_timestamp, bool show_keys,
setDebugParams_cb _hidl_cb)
{
return validateAndCall(
this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
&Supplicant::setDebugParamsInternal, _hidl_cb, level,
show_timestamp, show_keys);
}
Return<void> Supplicant::setConcurrencyPriority(
IfaceType type, setConcurrencyPriority_cb _hidl_cb)
{
return validateAndCall(
this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
&Supplicant::setConcurrencyPriorityInternal, _hidl_cb, type);
}
Return<ISupplicant::DebugLevel> Supplicant::getDebugLevel()
{
// TODO: Add SupplicantStatus in this method return for uniformity with
// the other methods in supplicant HIDL interface.
return (ISupplicant::DebugLevel)wpa_debug_level;
}
Return<bool> Supplicant::isDebugShowTimestampEnabled()
{
// TODO: Add SupplicantStatus in this method return for uniformity with
// the other methods in supplicant HIDL interface.
return ((wpa_debug_timestamp != 0) ? true : false);
}
Return<bool> Supplicant::isDebugShowKeysEnabled()
{
// TODO: Add SupplicantStatus in this method return for uniformity with
// the other methods in supplicant HIDL interface.
return ((wpa_debug_show_keys != 0) ? true : false);
}
Return<void> Supplicant::terminate()
{
wpa_printf(MSG_INFO, "Terminating...");
wpa_supplicant_terminate_proc(wpa_global_);
return Void();
}
std::pair<SupplicantStatus, sp<ISupplicantIface>>
Supplicant::addInterfaceInternal(const IfaceInfo& iface_info)
{
android::sp<ISupplicantIface> iface;
// Check if required |ifname| argument is empty.
if (iface_info.name.empty()) {
return {{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.
SupplicantStatus status;
std::tie(status, iface) = getInterfaceInternal(iface_info);
if (status.code == SupplicantStatusCode::SUCCESS) {
return {{SupplicantStatusCode::FAILURE_IFACE_EXISTS, ""},
iface};
}
struct wpa_interface iface_params = {};
iface_params.driver = kIfaceDriverName;
if (iface_info.type == IfaceType::P2P) {
if (ensureConfigFileExists(
kP2pIfaceConfPath, kOldP2pIfaceConfPath) != 0) {
wpa_printf(
MSG_ERROR, "Conf file does not exists: %s",
kP2pIfaceConfPath);
return {{SupplicantStatusCode::FAILURE_UNKNOWN,
"Conf file does not exist"},
{}};
}
iface_params.confname = kP2pIfaceConfPath;
int ret = access(kP2pIfaceConfOverlayPath, R_OK);
if (ret == 0) {
iface_params.confanother = kP2pIfaceConfOverlayPath;
}
} else {
if (ensureConfigFileExists(
kStaIfaceConfPath, kOldStaIfaceConfPath) != 0) {
wpa_printf(
MSG_ERROR, "Conf file does not exists: %s",
kStaIfaceConfPath);
return {{SupplicantStatusCode::FAILURE_UNKNOWN,
"Conf file does not exist"},
{}};
}
iface_params.confname = kStaIfaceConfPath;
int ret = access(kStaIfaceConfOverlayPath, R_OK);
if (ret == 0) {
iface_params.confanother = kStaIfaceConfOverlayPath;
}
}
iface_params.ifname = iface_info.name.c_str();
struct wpa_supplicant* wpa_s =
wpa_supplicant_add_iface(wpa_global_, &iface_params, NULL);
if (!wpa_s) {
return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
}
// The supplicant core creates a corresponding hidl object via
// HidlManager when |wpa_supplicant_add_iface| is called.
return getInterfaceInternal(iface_info);
}
SupplicantStatus 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 {SupplicantStatusCode::FAILURE_IFACE_UNKNOWN, ""};
}
if (wpa_supplicant_remove_iface(wpa_global_, wpa_s, 0)) {
return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
}
return {SupplicantStatusCode::SUCCESS, ""};
}
std::pair<SupplicantStatus, sp<ISupplicantIface>>
Supplicant::getInterfaceInternal(const IfaceInfo& iface_info)
{
struct wpa_supplicant* wpa_s =
wpa_supplicant_get_iface(wpa_global_, iface_info.name.c_str());
if (!wpa_s) {
return {{SupplicantStatusCode::FAILURE_IFACE_UNKNOWN, ""},
nullptr};
}
HidlManager* hidl_manager = HidlManager::getInstance();
if (iface_info.type == IfaceType::P2P) {
android::sp<ISupplicantP2pIface> iface;
if (!hidl_manager ||
hidl_manager->getP2pIfaceHidlObjectByIfname(
wpa_s->ifname, &iface)) {
return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""},
iface};
}
// Set this flag true here, since there is no HIDL 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 {{SupplicantStatusCode::SUCCESS, ""}, iface};
} else {
android::sp<ISupplicantStaIface> iface;
if (!hidl_manager ||
hidl_manager->getStaIfaceHidlObjectByIfname(
wpa_s->ifname, &iface)) {
return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""},
iface};
}
return {{SupplicantStatusCode::SUCCESS, ""}, iface};
}
}
std::pair<SupplicantStatus, std::vector<ISupplicant::IfaceInfo>>
Supplicant::listInterfacesInternal()
{
std::vector<ISupplicant::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(ISupplicant::IfaceInfo{
IfaceType::P2P, wpa_s->ifname});
} else {
ifaces.emplace_back(ISupplicant::IfaceInfo{
IfaceType::STA, wpa_s->ifname});
}
}
return {{SupplicantStatusCode::SUCCESS, ""}, std::move(ifaces)};
}
SupplicantStatus Supplicant::registerCallbackInternal(
const sp<ISupplicantCallback>& callback)
{
HidlManager* hidl_manager = HidlManager::getInstance();
if (!hidl_manager ||
hidl_manager->addSupplicantCallbackHidlObject(callback)) {
return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
}
return {SupplicantStatusCode::SUCCESS, ""};
}
SupplicantStatus Supplicant::setDebugParamsInternal(
ISupplicant::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 {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
}
return {SupplicantStatusCode::SUCCESS, ""};
}
SupplicantStatus 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 {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
}
return SupplicantStatus{SupplicantStatusCode::SUCCESS, ""};
}
} // namespace implementation
} // namespace V1_2
} // namespace supplicant
} // namespace wifi
} // namespace hardware
} // namespace android