This change takes care of setting the channel bandwidth when framework doesn't provide the bandwidth. Changes are, 1. 40MHz bandwidth in 2.4GHz band is not common and is prone to inter-op issues. So disable setting 40MHz in 2.4GHz band only configuration and cases where 60GHz is used. 2. Set 40MHz & vht operational channel width only in 5GHz and 6GHz when 11AC is enabled and 60GHz is not used cases. 3. set he operational channel width when 11ax is enabled and 60GHz is not used cases. Bug: 213122226 Test: atest android.net.wifi.cts.WifiManagerTest Test: adb shell cmd wifi force-softap-channel enabled 2412 Change-Id: I4e707d709f885daa5dcc7fdcafbfad751e3a62e1
1127 lines
33 KiB
C++
1127 lines
33 KiB
C++
/*
|
|
* aidl interface for wpa_hostapd daemon
|
|
* Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
|
|
* Copyright (c) 2004-2018, Roshan Pius <rpius@google.com>
|
|
*
|
|
* This software may be distributed under the terms of the BSD license.
|
|
* See README for more details.
|
|
*/
|
|
#include <iomanip>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <net/if.h>
|
|
#include <sys/socket.h>
|
|
#include <linux/if_bridge.h>
|
|
|
|
#include <android-base/file.h>
|
|
#include <android-base/stringprintf.h>
|
|
#include <android-base/unique_fd.h>
|
|
|
|
#include "hostapd.h"
|
|
#include <aidl/android/hardware/wifi/hostapd/ApInfo.h>
|
|
#include <aidl/android/hardware/wifi/hostapd/BandMask.h>
|
|
#include <aidl/android/hardware/wifi/hostapd/ChannelParams.h>
|
|
#include <aidl/android/hardware/wifi/hostapd/ClientInfo.h>
|
|
#include <aidl/android/hardware/wifi/hostapd/EncryptionType.h>
|
|
#include <aidl/android/hardware/wifi/hostapd/HostapdStatusCode.h>
|
|
#include <aidl/android/hardware/wifi/hostapd/IfaceParams.h>
|
|
#include <aidl/android/hardware/wifi/hostapd/NetworkParams.h>
|
|
#include <aidl/android/hardware/wifi/hostapd/ParamSizeLimits.h>
|
|
|
|
extern "C"
|
|
{
|
|
#include "common/wpa_ctrl.h"
|
|
#include "drivers/linux_ioctl.h"
|
|
}
|
|
|
|
// The AIDL implementation for hostapd creates a hostapd.conf dynamically for
|
|
// each interface. This file can then be used to hook onto the normal config
|
|
// file parsing logic in hostapd code. Helps us to avoid duplication of code
|
|
// in the AIDL interface.
|
|
// TOOD(b/71872409): Add unit tests for this.
|
|
namespace {
|
|
constexpr char kConfFileNameFmt[] = "/data/vendor/wifi/hostapd/hostapd_%s.conf";
|
|
|
|
using android::base::RemoveFileIfExists;
|
|
using android::base::StringPrintf;
|
|
using android::base::WriteStringToFile;
|
|
using aidl::android::hardware::wifi::hostapd::BandMask;
|
|
using aidl::android::hardware::wifi::hostapd::ChannelBandwidth;
|
|
using aidl::android::hardware::wifi::hostapd::ChannelParams;
|
|
using aidl::android::hardware::wifi::hostapd::EncryptionType;
|
|
using aidl::android::hardware::wifi::hostapd::Generation;
|
|
using aidl::android::hardware::wifi::hostapd::HostapdStatusCode;
|
|
using aidl::android::hardware::wifi::hostapd::IfaceParams;
|
|
using aidl::android::hardware::wifi::hostapd::NetworkParams;
|
|
using aidl::android::hardware::wifi::hostapd::ParamSizeLimits;
|
|
|
|
int band2Ghz = (int)BandMask::BAND_2_GHZ;
|
|
int band5Ghz = (int)BandMask::BAND_5_GHZ;
|
|
int band6Ghz = (int)BandMask::BAND_6_GHZ;
|
|
int band60Ghz = (int)BandMask::BAND_60_GHZ;
|
|
|
|
#define MAX_PORTS 1024
|
|
bool GetInterfacesInBridge(std::string br_name,
|
|
std::vector<std::string>* interfaces) {
|
|
android::base::unique_fd sock(socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
|
|
if (sock.get() < 0) {
|
|
wpa_printf(MSG_ERROR, "Failed to create sock (%s) in %s",
|
|
strerror(errno), __FUNCTION__);
|
|
return false;
|
|
}
|
|
|
|
struct ifreq request;
|
|
int i, ifindices[MAX_PORTS];
|
|
char if_name[IFNAMSIZ];
|
|
unsigned long args[3];
|
|
|
|
memset(ifindices, 0, MAX_PORTS * sizeof(int));
|
|
|
|
args[0] = BRCTL_GET_PORT_LIST;
|
|
args[1] = (unsigned long) ifindices;
|
|
args[2] = MAX_PORTS;
|
|
|
|
strlcpy(request.ifr_name, br_name.c_str(), IFNAMSIZ);
|
|
request.ifr_data = (char *)args;
|
|
|
|
if (ioctl(sock.get(), SIOCDEVPRIVATE, &request) < 0) {
|
|
wpa_printf(MSG_ERROR, "Failed to ioctl SIOCDEVPRIVATE in %s",
|
|
__FUNCTION__);
|
|
return false;
|
|
}
|
|
|
|
for (i = 0; i < MAX_PORTS; i ++) {
|
|
memset(if_name, 0, IFNAMSIZ);
|
|
if (ifindices[i] == 0 || !if_indextoname(ifindices[i], if_name)) {
|
|
continue;
|
|
}
|
|
interfaces->push_back(if_name);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::string WriteHostapdConfig(
|
|
const std::string& interface_name, const std::string& config)
|
|
{
|
|
const std::string file_path =
|
|
StringPrintf(kConfFileNameFmt, interface_name.c_str());
|
|
if (WriteStringToFile(
|
|
config, file_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
|
|
getuid(), getgid())) {
|
|
return file_path;
|
|
}
|
|
// Diagnose failure
|
|
int error = errno;
|
|
wpa_printf(
|
|
MSG_ERROR, "Cannot write hostapd config to %s, error: %s",
|
|
file_path.c_str(), strerror(error));
|
|
struct stat st;
|
|
int result = stat(file_path.c_str(), &st);
|
|
if (result == 0) {
|
|
wpa_printf(
|
|
MSG_ERROR, "hostapd config file uid: %d, gid: %d, mode: %d",
|
|
st.st_uid, st.st_gid, st.st_mode);
|
|
} else {
|
|
wpa_printf(
|
|
MSG_ERROR,
|
|
"Error calling stat() on hostapd config file: %s",
|
|
strerror(errno));
|
|
}
|
|
return "";
|
|
}
|
|
|
|
/*
|
|
* Get the op_class for a channel/band
|
|
* The logic here is based on Table E-4 in the 802.11 Specification
|
|
*/
|
|
int getOpClassForChannel(int channel, int band, bool support11n, bool support11ac) {
|
|
// 2GHz Band
|
|
if ((band & band2Ghz) != 0) {
|
|
if (channel == 14) {
|
|
return 82;
|
|
}
|
|
if (channel >= 1 && channel <= 13) {
|
|
if (!support11n) {
|
|
//20MHz channel
|
|
return 81;
|
|
}
|
|
if (channel <= 9) {
|
|
// HT40 with secondary channel above primary
|
|
return 83;
|
|
}
|
|
// HT40 with secondary channel below primary
|
|
return 84;
|
|
}
|
|
// Error
|
|
return 0;
|
|
}
|
|
|
|
// 5GHz Band
|
|
if ((band & band5Ghz) != 0) {
|
|
if (support11ac) {
|
|
switch (channel) {
|
|
case 42:
|
|
case 58:
|
|
case 106:
|
|
case 122:
|
|
case 138:
|
|
case 155:
|
|
// 80MHz channel
|
|
return 128;
|
|
case 50:
|
|
case 114:
|
|
// 160MHz channel
|
|
return 129;
|
|
}
|
|
}
|
|
|
|
if (!support11n) {
|
|
if (channel >= 36 && channel <= 48) {
|
|
return 115;
|
|
}
|
|
if (channel >= 52 && channel <= 64) {
|
|
return 118;
|
|
}
|
|
if (channel >= 100 && channel <= 144) {
|
|
return 121;
|
|
}
|
|
if (channel >= 149 && channel <= 161) {
|
|
return 124;
|
|
}
|
|
if (channel >= 165 && channel <= 169) {
|
|
return 125;
|
|
}
|
|
} else {
|
|
switch (channel) {
|
|
case 36:
|
|
case 44:
|
|
// HT40 with secondary channel above primary
|
|
return 116;
|
|
case 40:
|
|
case 48:
|
|
// HT40 with secondary channel below primary
|
|
return 117;
|
|
case 52:
|
|
case 60:
|
|
// HT40 with secondary channel above primary
|
|
return 119;
|
|
case 56:
|
|
case 64:
|
|
// HT40 with secondary channel below primary
|
|
return 120;
|
|
case 100:
|
|
case 108:
|
|
case 116:
|
|
case 124:
|
|
case 132:
|
|
case 140:
|
|
// HT40 with secondary channel above primary
|
|
return 122;
|
|
case 104:
|
|
case 112:
|
|
case 120:
|
|
case 128:
|
|
case 136:
|
|
case 144:
|
|
// HT40 with secondary channel below primary
|
|
return 123;
|
|
case 149:
|
|
case 157:
|
|
// HT40 with secondary channel above primary
|
|
return 126;
|
|
case 153:
|
|
case 161:
|
|
// HT40 with secondary channel below primary
|
|
return 127;
|
|
}
|
|
}
|
|
// Error
|
|
return 0;
|
|
}
|
|
|
|
// 6GHz Band
|
|
if ((band & band6Ghz) != 0) {
|
|
// Channels 1, 5. 9, 13, ...
|
|
if ((channel & 0x03) == 0x01) {
|
|
// 20MHz channel
|
|
return 131;
|
|
}
|
|
// Channels 3, 11, 19, 27, ...
|
|
if ((channel & 0x07) == 0x03) {
|
|
// 40MHz channel
|
|
return 132;
|
|
}
|
|
// Channels 7, 23, 39, 55, ...
|
|
if ((channel & 0x0F) == 0x07) {
|
|
// 80MHz channel
|
|
return 133;
|
|
}
|
|
// Channels 15, 47, 69, ...
|
|
if ((channel & 0x1F) == 0x0F) {
|
|
// 160MHz channel
|
|
return 134;
|
|
}
|
|
if (channel == 2) {
|
|
// 20MHz channel
|
|
return 136;
|
|
}
|
|
// Error
|
|
return 0;
|
|
}
|
|
|
|
if ((band & band60Ghz) != 0) {
|
|
if (1 <= channel && channel <= 8) {
|
|
return 180;
|
|
} else if (9 <= channel && channel <= 15) {
|
|
return 181;
|
|
} else if (17 <= channel && channel <= 22) {
|
|
return 182;
|
|
} else if (25 <= channel && channel <= 29) {
|
|
return 183;
|
|
}
|
|
// Error
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool validatePassphrase(int passphrase_len, int min_len, int max_len)
|
|
{
|
|
if (min_len != -1 && passphrase_len < min_len) return false;
|
|
if (max_len != -1 && passphrase_len > max_len) return false;
|
|
return true;
|
|
}
|
|
|
|
std::string CreateHostapdConfig(
|
|
const IfaceParams& iface_params,
|
|
const ChannelParams& channelParams,
|
|
const NetworkParams& nw_params,
|
|
const std::string br_name,
|
|
const std::string owe_transition_ifname)
|
|
{
|
|
if (nw_params.ssid.size() >
|
|
static_cast<uint32_t>(
|
|
ParamSizeLimits::SSID_MAX_LEN_IN_BYTES)) {
|
|
wpa_printf(
|
|
MSG_ERROR, "Invalid SSID size: %zu", nw_params.ssid.size());
|
|
return "";
|
|
}
|
|
|
|
// SSID string
|
|
std::stringstream ss;
|
|
ss << std::hex;
|
|
ss << std::setfill('0');
|
|
for (uint8_t b : nw_params.ssid) {
|
|
ss << std::setw(2) << static_cast<unsigned int>(b);
|
|
}
|
|
const std::string ssid_as_string = ss.str();
|
|
|
|
// Encryption config string
|
|
uint32_t band = 0;
|
|
band |= static_cast<uint32_t>(channelParams.bandMask);
|
|
bool is_2Ghz_band_only = band == static_cast<uint32_t>(band2Ghz);
|
|
bool is_6Ghz_band_only = band == static_cast<uint32_t>(band6Ghz);
|
|
bool is_60Ghz_band_only = band == static_cast<uint32_t>(band60Ghz);
|
|
std::string encryption_config_as_string;
|
|
switch (nw_params.encryptionType) {
|
|
case EncryptionType::NONE:
|
|
// no security params
|
|
break;
|
|
case EncryptionType::WPA:
|
|
if (!validatePassphrase(
|
|
nw_params.passphrase.size(),
|
|
static_cast<uint32_t>(ParamSizeLimits::
|
|
WPA2_PSK_PASSPHRASE_MIN_LEN_IN_BYTES),
|
|
static_cast<uint32_t>(ParamSizeLimits::
|
|
WPA2_PSK_PASSPHRASE_MAX_LEN_IN_BYTES))) {
|
|
return "";
|
|
}
|
|
encryption_config_as_string = StringPrintf(
|
|
"wpa=3\n"
|
|
"wpa_pairwise=%s\n"
|
|
"wpa_passphrase=%s",
|
|
is_60Ghz_band_only ? "GCMP" : "TKIP CCMP",
|
|
nw_params.passphrase.c_str());
|
|
break;
|
|
case EncryptionType::WPA2:
|
|
if (!validatePassphrase(
|
|
nw_params.passphrase.size(),
|
|
static_cast<uint32_t>(ParamSizeLimits::
|
|
WPA2_PSK_PASSPHRASE_MIN_LEN_IN_BYTES),
|
|
static_cast<uint32_t>(ParamSizeLimits::
|
|
WPA2_PSK_PASSPHRASE_MAX_LEN_IN_BYTES))) {
|
|
return "";
|
|
}
|
|
encryption_config_as_string = StringPrintf(
|
|
"wpa=2\n"
|
|
"rsn_pairwise=%s\n"
|
|
#ifdef ENABLE_HOSTAPD_CONFIG_80211W_MFP_OPTIONAL
|
|
"ieee80211w=1\n"
|
|
#endif
|
|
"wpa_passphrase=%s",
|
|
is_60Ghz_band_only ? "GCMP" : "CCMP",
|
|
nw_params.passphrase.c_str());
|
|
break;
|
|
case EncryptionType::WPA3_SAE_TRANSITION:
|
|
if (!validatePassphrase(
|
|
nw_params.passphrase.size(),
|
|
static_cast<uint32_t>(ParamSizeLimits::
|
|
WPA2_PSK_PASSPHRASE_MIN_LEN_IN_BYTES),
|
|
static_cast<uint32_t>(ParamSizeLimits::
|
|
WPA2_PSK_PASSPHRASE_MAX_LEN_IN_BYTES))) {
|
|
return "";
|
|
}
|
|
encryption_config_as_string = StringPrintf(
|
|
"wpa=2\n"
|
|
"rsn_pairwise=%s\n"
|
|
"wpa_key_mgmt=WPA-PSK SAE\n"
|
|
"ieee80211w=1\n"
|
|
"sae_require_mfp=1\n"
|
|
"wpa_passphrase=%s\n"
|
|
"sae_password=%s",
|
|
is_60Ghz_band_only ? "GCMP" : "CCMP",
|
|
nw_params.passphrase.c_str(),
|
|
nw_params.passphrase.c_str());
|
|
break;
|
|
case EncryptionType::WPA3_SAE:
|
|
if (!validatePassphrase(nw_params.passphrase.size(), 1, -1)) {
|
|
return "";
|
|
}
|
|
encryption_config_as_string = StringPrintf(
|
|
"wpa=2\n"
|
|
"rsn_pairwise=%s\n"
|
|
"wpa_key_mgmt=SAE\n"
|
|
"ieee80211w=2\n"
|
|
"sae_require_mfp=2\n"
|
|
"sae_pwe=%d\n"
|
|
"sae_password=%s",
|
|
is_60Ghz_band_only ? "GCMP" : "CCMP",
|
|
is_6Ghz_band_only ? 1 : 2,
|
|
nw_params.passphrase.c_str());
|
|
break;
|
|
case EncryptionType::WPA3_OWE_TRANSITION:
|
|
encryption_config_as_string = StringPrintf(
|
|
"wpa=2\n"
|
|
"rsn_pairwise=%s\n"
|
|
"wpa_key_mgmt=OWE\n"
|
|
"ieee80211w=2",
|
|
is_60Ghz_band_only ? "GCMP" : "CCMP");
|
|
break;
|
|
case EncryptionType::WPA3_OWE:
|
|
encryption_config_as_string = StringPrintf(
|
|
"wpa=2\n"
|
|
"rsn_pairwise=%s\n"
|
|
"wpa_key_mgmt=OWE\n"
|
|
"ieee80211w=2",
|
|
is_60Ghz_band_only ? "GCMP" : "CCMP");
|
|
break;
|
|
default:
|
|
wpa_printf(MSG_ERROR, "Unknown encryption type");
|
|
return "";
|
|
}
|
|
|
|
std::string channel_config_as_string;
|
|
bool isFirst = true;
|
|
if (channelParams.enableAcs) {
|
|
std::string freqList_as_string;
|
|
for (const auto &range :
|
|
channelParams.acsChannelFreqRangesMhz) {
|
|
if (!isFirst) {
|
|
freqList_as_string += ",";
|
|
}
|
|
isFirst = false;
|
|
|
|
if (range.startMhz != range.endMhz) {
|
|
freqList_as_string +=
|
|
StringPrintf("%d-%d", range.startMhz, range.endMhz);
|
|
} else {
|
|
freqList_as_string += StringPrintf("%d", range.startMhz);
|
|
}
|
|
}
|
|
channel_config_as_string = StringPrintf(
|
|
"channel=0\n"
|
|
"acs_exclude_dfs=%d\n"
|
|
"freqlist=%s",
|
|
channelParams.acsShouldExcludeDfs,
|
|
freqList_as_string.c_str());
|
|
} else {
|
|
int op_class = getOpClassForChannel(
|
|
channelParams.channel,
|
|
band,
|
|
iface_params.hwModeParams.enable80211N,
|
|
iface_params.hwModeParams.enable80211AC);
|
|
channel_config_as_string = StringPrintf(
|
|
"channel=%d\n"
|
|
"op_class=%d",
|
|
channelParams.channel, op_class);
|
|
}
|
|
|
|
std::string hw_mode_as_string;
|
|
std::string enable_edmg_as_string;
|
|
std::string edmg_channel_as_string;
|
|
bool is_60Ghz_used = false;
|
|
|
|
if (((band & band60Ghz) != 0)) {
|
|
hw_mode_as_string = "hw_mode=ad";
|
|
if (iface_params.hwModeParams.enableEdmg) {
|
|
enable_edmg_as_string = "enable_edmg=1";
|
|
edmg_channel_as_string = StringPrintf(
|
|
"edmg_channel=%d",
|
|
channelParams.channel);
|
|
}
|
|
is_60Ghz_used = true;
|
|
} else if ((band & band2Ghz) != 0) {
|
|
if (((band & band5Ghz) != 0)
|
|
|| ((band & band6Ghz) != 0)) {
|
|
hw_mode_as_string = "hw_mode=any";
|
|
} else {
|
|
hw_mode_as_string = "hw_mode=g";
|
|
}
|
|
} else if (((band & band5Ghz) != 0)
|
|
|| ((band & band6Ghz) != 0)) {
|
|
hw_mode_as_string = "hw_mode=a";
|
|
} else {
|
|
wpa_printf(MSG_ERROR, "Invalid band");
|
|
return "";
|
|
}
|
|
|
|
std::string he_params_as_string;
|
|
#ifdef CONFIG_IEEE80211AX
|
|
if (iface_params.hwModeParams.enable80211AX && !is_60Ghz_used) {
|
|
he_params_as_string = StringPrintf(
|
|
"ieee80211ax=1\n"
|
|
"he_su_beamformer=%d\n"
|
|
"he_su_beamformee=%d\n"
|
|
"he_mu_beamformer=%d\n"
|
|
"he_twt_required=%d\n",
|
|
iface_params.hwModeParams.enableHeSingleUserBeamformer ? 1 : 0,
|
|
iface_params.hwModeParams.enableHeSingleUserBeamformee ? 1 : 0,
|
|
iface_params.hwModeParams.enableHeMultiUserBeamformer ? 1 : 0,
|
|
iface_params.hwModeParams.enableHeTargetWakeTime ? 1 : 0);
|
|
} else {
|
|
he_params_as_string = "ieee80211ax=0";
|
|
}
|
|
#endif /* CONFIG_IEEE80211AX */
|
|
|
|
std::string ht_cap_vht_oper_he_oper_chwidth_as_string;
|
|
switch (iface_params.hwModeParams.maximumChannelBandwidth) {
|
|
case ChannelBandwidth::BANDWIDTH_20:
|
|
ht_cap_vht_oper_he_oper_chwidth_as_string = StringPrintf(
|
|
#ifdef CONFIG_IEEE80211AX
|
|
"he_oper_chwidth=0\n"
|
|
#endif
|
|
"vht_oper_chwidth=0");
|
|
break;
|
|
case ChannelBandwidth::BANDWIDTH_40:
|
|
ht_cap_vht_oper_he_oper_chwidth_as_string = StringPrintf(
|
|
"ht_capab=[HT40+]\n"
|
|
#ifdef CONFIG_IEEE80211AX
|
|
"he_oper_chwidth=0\n"
|
|
#endif
|
|
"vht_oper_chwidth=0");
|
|
break;
|
|
case ChannelBandwidth::BANDWIDTH_80:
|
|
ht_cap_vht_oper_he_oper_chwidth_as_string = StringPrintf(
|
|
"ht_capab=[HT40+]\n"
|
|
#ifdef CONFIG_IEEE80211AX
|
|
"he_oper_chwidth=%d\n"
|
|
#endif
|
|
"vht_oper_chwidth=%d",
|
|
#ifdef CONFIG_IEEE80211AX
|
|
(iface_params.hwModeParams.enable80211AX && !is_60Ghz_used) ? 1 : 0,
|
|
#endif
|
|
iface_params.hwModeParams.enable80211AC ? 1 : 0);
|
|
break;
|
|
case ChannelBandwidth::BANDWIDTH_160:
|
|
ht_cap_vht_oper_he_oper_chwidth_as_string = StringPrintf(
|
|
"ht_capab=[HT40+]\n"
|
|
#ifdef CONFIG_IEEE80211AX
|
|
"he_oper_chwidth=%d\n"
|
|
#endif
|
|
"vht_oper_chwidth=%d",
|
|
#ifdef CONFIG_IEEE80211AX
|
|
(iface_params.hwModeParams.enable80211AX && !is_60Ghz_used) ? 2 : 0,
|
|
#endif
|
|
iface_params.hwModeParams.enable80211AC ? 2 : 0);
|
|
break;
|
|
default:
|
|
if (!is_2Ghz_band_only && !is_60Ghz_used
|
|
&& iface_params.hwModeParams.enable80211AC) {
|
|
ht_cap_vht_oper_he_oper_chwidth_as_string =
|
|
"ht_capab=[HT40+]\n"
|
|
"vht_oper_chwidth=1\n";
|
|
}
|
|
#ifdef CONFIG_IEEE80211AX
|
|
if (iface_params.hwModeParams.enable80211AX && !is_60Ghz_used) {
|
|
ht_cap_vht_oper_he_oper_chwidth_as_string += "he_oper_chwidth=1";
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
#ifdef CONFIG_INTERWORKING
|
|
std::string access_network_params_as_string;
|
|
if (nw_params.isMetered) {
|
|
access_network_params_as_string = StringPrintf(
|
|
"interworking=1\n"
|
|
"access_network_type=2\n"); // CHARGEABLE_PUBLIC_NETWORK
|
|
} else {
|
|
access_network_params_as_string = StringPrintf(
|
|
"interworking=0\n");
|
|
}
|
|
#endif /* CONFIG_INTERWORKING */
|
|
|
|
std::string bridge_as_string;
|
|
if (!br_name.empty()) {
|
|
bridge_as_string = StringPrintf("bridge=%s", br_name.c_str());
|
|
}
|
|
|
|
// vendor_elements string
|
|
std::string vendor_elements_as_string;
|
|
if (nw_params.vendorElements.size() > 0) {
|
|
std::stringstream ss;
|
|
ss << std::hex;
|
|
ss << std::setfill('0');
|
|
for (uint8_t b : nw_params.vendorElements) {
|
|
ss << std::setw(2) << static_cast<unsigned int>(b);
|
|
}
|
|
vendor_elements_as_string = StringPrintf("vendor_elements=%s", ss.str().c_str());
|
|
}
|
|
|
|
std::string owe_transition_ifname_as_string;
|
|
if (!owe_transition_ifname.empty()) {
|
|
owe_transition_ifname_as_string = StringPrintf(
|
|
"owe_transition_ifname=%s", owe_transition_ifname.c_str());
|
|
}
|
|
|
|
return StringPrintf(
|
|
"interface=%s\n"
|
|
"driver=nl80211\n"
|
|
"ctrl_interface=/data/vendor/wifi/hostapd/ctrl\n"
|
|
// ssid2 signals to hostapd that the value is not a literal value
|
|
// for use as a SSID. In this case, we're giving it a hex
|
|
// std::string and hostapd needs to expect that.
|
|
"ssid2=%s\n"
|
|
"%s\n"
|
|
"ieee80211n=%d\n"
|
|
"ieee80211ac=%d\n"
|
|
"%s\n"
|
|
"%s\n"
|
|
"%s\n"
|
|
"ignore_broadcast_ssid=%d\n"
|
|
"wowlan_triggers=any\n"
|
|
#ifdef CONFIG_INTERWORKING
|
|
"%s\n"
|
|
#endif /* CONFIG_INTERWORKING */
|
|
"%s\n"
|
|
"%s\n"
|
|
"%s\n"
|
|
"%s\n"
|
|
"%s\n"
|
|
"%s\n",
|
|
iface_params.name.c_str(), ssid_as_string.c_str(),
|
|
channel_config_as_string.c_str(),
|
|
iface_params.hwModeParams.enable80211N ? 1 : 0,
|
|
iface_params.hwModeParams.enable80211AC ? 1 : 0,
|
|
he_params_as_string.c_str(),
|
|
hw_mode_as_string.c_str(), ht_cap_vht_oper_he_oper_chwidth_as_string.c_str(),
|
|
nw_params.isHidden ? 1 : 0,
|
|
#ifdef CONFIG_INTERWORKING
|
|
access_network_params_as_string.c_str(),
|
|
#endif /* CONFIG_INTERWORKING */
|
|
encryption_config_as_string.c_str(),
|
|
bridge_as_string.c_str(),
|
|
owe_transition_ifname_as_string.c_str(),
|
|
enable_edmg_as_string.c_str(),
|
|
edmg_channel_as_string.c_str(),
|
|
vendor_elements_as_string.c_str());
|
|
}
|
|
|
|
Generation getGeneration(hostapd_hw_modes *current_mode)
|
|
{
|
|
wpa_printf(MSG_DEBUG, "getGeneration hwmode=%d, ht_enabled=%d,"
|
|
" vht_enabled=%d, he_supported=%d",
|
|
current_mode->mode, current_mode->ht_capab != 0,
|
|
current_mode->vht_capab != 0, current_mode->he_capab->he_supported);
|
|
switch (current_mode->mode) {
|
|
case HOSTAPD_MODE_IEEE80211B:
|
|
return Generation::WIFI_STANDARD_LEGACY;
|
|
case HOSTAPD_MODE_IEEE80211G:
|
|
return current_mode->ht_capab == 0 ?
|
|
Generation::WIFI_STANDARD_LEGACY : Generation::WIFI_STANDARD_11N;
|
|
case HOSTAPD_MODE_IEEE80211A:
|
|
if (current_mode->he_capab->he_supported) {
|
|
return Generation::WIFI_STANDARD_11AX;
|
|
}
|
|
return current_mode->vht_capab == 0 ?
|
|
Generation::WIFI_STANDARD_11N : Generation::WIFI_STANDARD_11AC;
|
|
case HOSTAPD_MODE_IEEE80211AD:
|
|
return Generation::WIFI_STANDARD_11AD;
|
|
default:
|
|
return Generation::WIFI_STANDARD_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
ChannelBandwidth getChannelBandwidth(struct hostapd_config *iconf)
|
|
{
|
|
wpa_printf(MSG_DEBUG, "getChannelBandwidth %d, isHT=%d, isHT40=%d",
|
|
iconf->vht_oper_chwidth, iconf->ieee80211n,
|
|
iconf->secondary_channel);
|
|
switch (iconf->vht_oper_chwidth) {
|
|
case CHANWIDTH_80MHZ:
|
|
return ChannelBandwidth::BANDWIDTH_80;
|
|
case CHANWIDTH_80P80MHZ:
|
|
return ChannelBandwidth::BANDWIDTH_80P80;
|
|
break;
|
|
case CHANWIDTH_160MHZ:
|
|
return ChannelBandwidth::BANDWIDTH_160;
|
|
break;
|
|
case CHANWIDTH_USE_HT:
|
|
if (iconf->ieee80211n) {
|
|
return iconf->secondary_channel != 0 ?
|
|
ChannelBandwidth::BANDWIDTH_40 : ChannelBandwidth::BANDWIDTH_20;
|
|
}
|
|
return ChannelBandwidth::BANDWIDTH_20_NOHT;
|
|
case CHANWIDTH_2160MHZ:
|
|
return ChannelBandwidth::BANDWIDTH_2160;
|
|
case CHANWIDTH_4320MHZ:
|
|
return ChannelBandwidth::BANDWIDTH_4320;
|
|
case CHANWIDTH_6480MHZ:
|
|
return ChannelBandwidth::BANDWIDTH_6480;
|
|
case CHANWIDTH_8640MHZ:
|
|
return ChannelBandwidth::BANDWIDTH_8640;
|
|
default:
|
|
return ChannelBandwidth::BANDWIDTH_INVALID;
|
|
}
|
|
}
|
|
|
|
bool forceStaDisconnection(struct hostapd_data* hapd,
|
|
const std::vector<uint8_t>& client_address,
|
|
const uint16_t reason_code) {
|
|
struct sta_info *sta;
|
|
for (sta = hapd->sta_list; sta; sta = sta->next) {
|
|
int res;
|
|
res = memcmp(sta->addr, client_address.data(), ETH_ALEN);
|
|
if (res == 0) {
|
|
wpa_printf(MSG_INFO, "Force client:" MACSTR " disconnect with reason: %d",
|
|
MAC2STR(client_address.data()), reason_code);
|
|
ap_sta_disconnect(hapd, sta, sta->addr, reason_code);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// hostapd core functions accept "C" style function pointers, so use global
|
|
// functions to pass to the hostapd core function and store the corresponding
|
|
// std::function methods to be invoked.
|
|
//
|
|
// NOTE: Using the pattern from the vendor HAL (wifi_legacy_hal.cpp).
|
|
//
|
|
// Callback to be invoked once setup is complete
|
|
std::function<void(struct hostapd_data*)> on_setup_complete_internal_callback;
|
|
void onAsyncSetupCompleteCb(void* ctx)
|
|
{
|
|
struct hostapd_data* iface_hapd = (struct hostapd_data*)ctx;
|
|
if (on_setup_complete_internal_callback) {
|
|
on_setup_complete_internal_callback(iface_hapd);
|
|
// Invalidate this callback since we don't want this firing
|
|
// again in single AP mode.
|
|
if (strlen(iface_hapd->conf->bridge) > 0) {
|
|
on_setup_complete_internal_callback = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Callback to be invoked on hotspot client connection/disconnection
|
|
std::function<void(struct hostapd_data*, const u8 *mac_addr, int authorized,
|
|
const u8 *p2p_dev_addr)> on_sta_authorized_internal_callback;
|
|
void onAsyncStaAuthorizedCb(void* ctx, const u8 *mac_addr, int authorized,
|
|
const u8 *p2p_dev_addr)
|
|
{
|
|
struct hostapd_data* iface_hapd = (struct hostapd_data*)ctx;
|
|
if (on_sta_authorized_internal_callback) {
|
|
on_sta_authorized_internal_callback(iface_hapd, mac_addr,
|
|
authorized, p2p_dev_addr);
|
|
}
|
|
}
|
|
|
|
std::function<void(struct hostapd_data*, int level,
|
|
enum wpa_msg_type type, const char *txt,
|
|
size_t len)> on_wpa_msg_internal_callback;
|
|
|
|
void onAsyncWpaEventCb(void *ctx, int level,
|
|
enum wpa_msg_type type, const char *txt,
|
|
size_t len)
|
|
{
|
|
struct hostapd_data* iface_hapd = (struct hostapd_data*)ctx;
|
|
if (on_wpa_msg_internal_callback) {
|
|
on_wpa_msg_internal_callback(iface_hapd, level,
|
|
type, txt, len);
|
|
}
|
|
}
|
|
|
|
inline ndk::ScopedAStatus createStatus(HostapdStatusCode status_code) {
|
|
return ndk::ScopedAStatus::fromServiceSpecificError(
|
|
static_cast<int32_t>(status_code));
|
|
}
|
|
|
|
inline ndk::ScopedAStatus createStatusWithMsg(
|
|
HostapdStatusCode status_code, std::string msg)
|
|
{
|
|
return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
|
|
static_cast<int32_t>(status_code), msg.c_str());
|
|
}
|
|
|
|
// Method called by death_notifier_ on client death.
|
|
void onDeath(void* cookie) {
|
|
wpa_printf(MSG_ERROR, "Client died. Terminating...");
|
|
eloop_terminate();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace aidl {
|
|
namespace android {
|
|
namespace hardware {
|
|
namespace wifi {
|
|
namespace hostapd {
|
|
|
|
Hostapd::Hostapd(struct hapd_interfaces* interfaces)
|
|
: interfaces_(interfaces)
|
|
{
|
|
death_notifier_ = AIBinder_DeathRecipient_new(onDeath);
|
|
}
|
|
|
|
::ndk::ScopedAStatus Hostapd::addAccessPoint(
|
|
const IfaceParams& iface_params, const NetworkParams& nw_params)
|
|
{
|
|
return addAccessPointInternal(iface_params, nw_params);
|
|
}
|
|
|
|
::ndk::ScopedAStatus Hostapd::removeAccessPoint(const std::string& iface_name)
|
|
{
|
|
return removeAccessPointInternal(iface_name);
|
|
}
|
|
|
|
::ndk::ScopedAStatus Hostapd::terminate()
|
|
{
|
|
wpa_printf(MSG_INFO, "Terminating...");
|
|
// Clear the callback to avoid IPCThreadState shutdown during the
|
|
// callback event.
|
|
callbacks_.clear();
|
|
eloop_terminate();
|
|
return ndk::ScopedAStatus::ok();
|
|
}
|
|
|
|
::ndk::ScopedAStatus Hostapd::registerCallback(
|
|
const std::shared_ptr<IHostapdCallback>& callback)
|
|
{
|
|
return registerCallbackInternal(callback);
|
|
}
|
|
|
|
::ndk::ScopedAStatus Hostapd::forceClientDisconnect(
|
|
const std::string& iface_name, const std::vector<uint8_t>& client_address,
|
|
Ieee80211ReasonCode reason_code)
|
|
{
|
|
return forceClientDisconnectInternal(iface_name, client_address, reason_code);
|
|
}
|
|
|
|
::ndk::ScopedAStatus Hostapd::setDebugParams(DebugLevel level)
|
|
{
|
|
return setDebugParamsInternal(level);
|
|
}
|
|
|
|
::ndk::ScopedAStatus Hostapd::addAccessPointInternal(
|
|
const IfaceParams& iface_params,
|
|
const NetworkParams& nw_params)
|
|
{
|
|
int channelParamsSize = iface_params.channelParams.size();
|
|
if (channelParamsSize == 1) {
|
|
// Single AP
|
|
wpa_printf(MSG_INFO, "AddSingleAccessPoint, iface=%s",
|
|
iface_params.name.c_str());
|
|
return addSingleAccessPoint(iface_params, iface_params.channelParams[0],
|
|
nw_params, "", "");
|
|
} else if (channelParamsSize == 2) {
|
|
// Concurrent APs
|
|
wpa_printf(MSG_INFO, "AddDualAccessPoint, iface=%s",
|
|
iface_params.name.c_str());
|
|
return addConcurrentAccessPoints(iface_params, nw_params);
|
|
}
|
|
return createStatus(HostapdStatusCode::FAILURE_ARGS_INVALID);
|
|
}
|
|
|
|
std::vector<uint8_t> generateRandomOweSsid()
|
|
{
|
|
u8 random[8] = {0};
|
|
os_get_random(random, 8);
|
|
|
|
std::string ssid = StringPrintf("Owe-%s", random);
|
|
wpa_printf(MSG_INFO, "Generated OWE SSID: %s", ssid.c_str());
|
|
std::vector<uint8_t> vssid(ssid.begin(), ssid.end());
|
|
|
|
return vssid;
|
|
}
|
|
|
|
::ndk::ScopedAStatus Hostapd::addConcurrentAccessPoints(
|
|
const IfaceParams& iface_params, const NetworkParams& nw_params)
|
|
{
|
|
int channelParamsListSize = iface_params.channelParams.size();
|
|
// Get available interfaces in bridge
|
|
std::vector<std::string> managed_interfaces;
|
|
std::string br_name = StringPrintf(
|
|
"%s", iface_params.name.c_str());
|
|
if (!GetInterfacesInBridge(br_name, &managed_interfaces)) {
|
|
return createStatusWithMsg(HostapdStatusCode::FAILURE_UNKNOWN,
|
|
"Get interfaces in bridge failed.");
|
|
}
|
|
if (managed_interfaces.size() < channelParamsListSize) {
|
|
return createStatusWithMsg(HostapdStatusCode::FAILURE_UNKNOWN,
|
|
"Available interfaces less than requested bands");
|
|
}
|
|
// start BSS on specified bands
|
|
for (std::size_t i = 0; i < channelParamsListSize; i ++) {
|
|
IfaceParams iface_params_new = iface_params;
|
|
NetworkParams nw_params_new = nw_params;
|
|
iface_params_new.name = managed_interfaces[i];
|
|
|
|
std::string owe_transition_ifname = "";
|
|
if (nw_params.encryptionType == EncryptionType::WPA3_OWE_TRANSITION) {
|
|
if (i == 0 && i+1 < channelParamsListSize) {
|
|
owe_transition_ifname = managed_interfaces[i+1];
|
|
nw_params_new.encryptionType = EncryptionType::NONE;
|
|
} else {
|
|
owe_transition_ifname = managed_interfaces[0];
|
|
nw_params_new.isHidden = true;
|
|
nw_params_new.ssid = generateRandomOweSsid();
|
|
}
|
|
}
|
|
|
|
ndk::ScopedAStatus status = addSingleAccessPoint(
|
|
iface_params_new, iface_params.channelParams[i], nw_params_new,
|
|
br_name, owe_transition_ifname);
|
|
if (!status.isOk()) {
|
|
wpa_printf(MSG_ERROR, "Failed to addAccessPoint %s",
|
|
managed_interfaces[i].c_str());
|
|
return status;
|
|
}
|
|
}
|
|
// Save bridge interface info
|
|
br_interfaces_[br_name] = managed_interfaces;
|
|
return ndk::ScopedAStatus::ok();
|
|
}
|
|
|
|
::ndk::ScopedAStatus Hostapd::addSingleAccessPoint(
|
|
const IfaceParams& iface_params,
|
|
const ChannelParams& channelParams,
|
|
const NetworkParams& nw_params,
|
|
const std::string br_name,
|
|
const std::string owe_transition_ifname)
|
|
{
|
|
if (hostapd_get_iface(interfaces_, iface_params.name.c_str())) {
|
|
wpa_printf(
|
|
MSG_ERROR, "Interface %s already present",
|
|
iface_params.name.c_str());
|
|
return createStatus(HostapdStatusCode::FAILURE_IFACE_EXISTS);
|
|
}
|
|
const auto conf_params = CreateHostapdConfig(iface_params, channelParams, nw_params,
|
|
br_name, owe_transition_ifname);
|
|
if (conf_params.empty()) {
|
|
wpa_printf(MSG_ERROR, "Failed to create config params");
|
|
return createStatus(HostapdStatusCode::FAILURE_ARGS_INVALID);
|
|
}
|
|
const auto conf_file_path =
|
|
WriteHostapdConfig(iface_params.name, conf_params);
|
|
if (conf_file_path.empty()) {
|
|
wpa_printf(MSG_ERROR, "Failed to write config file");
|
|
return createStatus(HostapdStatusCode::FAILURE_UNKNOWN);
|
|
}
|
|
std::string add_iface_param_str = StringPrintf(
|
|
"%s config=%s", iface_params.name.c_str(),
|
|
conf_file_path.c_str());
|
|
std::vector<char> add_iface_param_vec(
|
|
add_iface_param_str.begin(), add_iface_param_str.end() + 1);
|
|
if (hostapd_add_iface(interfaces_, add_iface_param_vec.data()) < 0) {
|
|
wpa_printf(
|
|
MSG_ERROR, "Adding interface %s failed",
|
|
add_iface_param_str.c_str());
|
|
return createStatus(HostapdStatusCode::FAILURE_UNKNOWN);
|
|
}
|
|
struct hostapd_data* iface_hapd =
|
|
hostapd_get_iface(interfaces_, iface_params.name.c_str());
|
|
WPA_ASSERT(iface_hapd != nullptr && iface_hapd->iface != nullptr);
|
|
// Register the setup complete callbacks
|
|
on_setup_complete_internal_callback =
|
|
[this](struct hostapd_data* iface_hapd) {
|
|
wpa_printf(
|
|
MSG_INFO, "AP interface setup completed - state %s",
|
|
hostapd_state_text(iface_hapd->iface->state));
|
|
if (iface_hapd->iface->state == HAPD_IFACE_DISABLED) {
|
|
// Invoke the failure callback on all registered
|
|
// clients.
|
|
for (const auto& callback : callbacks_) {
|
|
callback->onFailure(strlen(iface_hapd->conf->bridge) > 0 ?
|
|
iface_hapd->conf->bridge : iface_hapd->conf->iface,
|
|
iface_hapd->conf->iface);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Register for new client connect/disconnect indication.
|
|
on_sta_authorized_internal_callback =
|
|
[this](struct hostapd_data* iface_hapd, const u8 *mac_addr,
|
|
int authorized, const u8 *p2p_dev_addr) {
|
|
wpa_printf(MSG_DEBUG, "notify client " MACSTR " %s",
|
|
MAC2STR(mac_addr),
|
|
(authorized) ? "Connected" : "Disconnected");
|
|
ClientInfo info;
|
|
info.ifaceName = strlen(iface_hapd->conf->bridge) > 0 ?
|
|
iface_hapd->conf->bridge : iface_hapd->conf->iface;
|
|
info.apIfaceInstance = iface_hapd->conf->iface;
|
|
info.clientAddress.assign(mac_addr, mac_addr + ETH_ALEN);
|
|
info.isConnected = authorized;
|
|
for (const auto &callback : callbacks_) {
|
|
callback->onConnectedClientsChanged(info);
|
|
}
|
|
};
|
|
|
|
// Register for wpa_event which used to get channel switch event
|
|
on_wpa_msg_internal_callback =
|
|
[this](struct hostapd_data* iface_hapd, int level,
|
|
enum wpa_msg_type type, const char *txt,
|
|
size_t len) {
|
|
wpa_printf(MSG_DEBUG, "Receive wpa msg : %s", txt);
|
|
if (os_strncmp(txt, AP_EVENT_ENABLED,
|
|
strlen(AP_EVENT_ENABLED)) == 0 ||
|
|
os_strncmp(txt, WPA_EVENT_CHANNEL_SWITCH,
|
|
strlen(WPA_EVENT_CHANNEL_SWITCH)) == 0) {
|
|
ApInfo info;
|
|
info.ifaceName = strlen(iface_hapd->conf->bridge) > 0 ?
|
|
iface_hapd->conf->bridge : iface_hapd->conf->iface,
|
|
info.apIfaceInstance = iface_hapd->conf->iface;
|
|
info.freqMhz = iface_hapd->iface->freq;
|
|
info.channelBandwidth = getChannelBandwidth(iface_hapd->iconf);
|
|
info.generation = getGeneration(iface_hapd->iface->current_mode);
|
|
info.apIfaceInstanceMacAddress.assign(iface_hapd->own_addr,
|
|
iface_hapd->own_addr + ETH_ALEN);
|
|
for (const auto &callback : callbacks_) {
|
|
callback->onApInstanceInfoChanged(info);
|
|
}
|
|
} else if (os_strncmp(txt, AP_EVENT_DISABLED, strlen(AP_EVENT_DISABLED)) == 0) {
|
|
// Invoke the failure callback on all registered clients.
|
|
for (const auto& callback : callbacks_) {
|
|
callback->onFailure(strlen(iface_hapd->conf->bridge) > 0 ?
|
|
iface_hapd->conf->bridge : iface_hapd->conf->iface,
|
|
iface_hapd->conf->iface);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Setup callback
|
|
iface_hapd->setup_complete_cb = onAsyncSetupCompleteCb;
|
|
iface_hapd->setup_complete_cb_ctx = iface_hapd;
|
|
iface_hapd->sta_authorized_cb = onAsyncStaAuthorizedCb;
|
|
iface_hapd->sta_authorized_cb_ctx = iface_hapd;
|
|
wpa_msg_register_cb(onAsyncWpaEventCb);
|
|
|
|
if (hostapd_enable_iface(iface_hapd->iface) < 0) {
|
|
wpa_printf(
|
|
MSG_ERROR, "Enabling interface %s failed",
|
|
iface_params.name.c_str());
|
|
return createStatus(HostapdStatusCode::FAILURE_UNKNOWN);
|
|
}
|
|
return ndk::ScopedAStatus::ok();
|
|
}
|
|
|
|
::ndk::ScopedAStatus Hostapd::removeAccessPointInternal(const std::string& iface_name)
|
|
{
|
|
// interfaces to be removed
|
|
std::vector<std::string> interfaces;
|
|
bool is_error = false;
|
|
|
|
const auto it = br_interfaces_.find(iface_name);
|
|
if (it != br_interfaces_.end()) {
|
|
// In case bridge, remove managed interfaces
|
|
interfaces = it->second;
|
|
br_interfaces_.erase(iface_name);
|
|
} else {
|
|
// else remove current interface
|
|
interfaces.push_back(iface_name);
|
|
}
|
|
|
|
for (auto& iface : interfaces) {
|
|
std::vector<char> remove_iface_param_vec(
|
|
iface.begin(), iface.end() + 1);
|
|
if (hostapd_remove_iface(interfaces_, remove_iface_param_vec.data()) < 0) {
|
|
wpa_printf(MSG_INFO, "Remove interface %s failed", iface.c_str());
|
|
is_error = true;
|
|
}
|
|
}
|
|
if (is_error) {
|
|
return createStatus(HostapdStatusCode::FAILURE_UNKNOWN);
|
|
}
|
|
return ndk::ScopedAStatus::ok();
|
|
}
|
|
|
|
::ndk::ScopedAStatus Hostapd::registerCallbackInternal(
|
|
const std::shared_ptr<IHostapdCallback>& callback)
|
|
{
|
|
binder_status_t status = AIBinder_linkToDeath(callback->asBinder().get(),
|
|
death_notifier_, this /* cookie */);
|
|
if (status != STATUS_OK) {
|
|
wpa_printf(
|
|
MSG_ERROR,
|
|
"Error registering for death notification for "
|
|
"hostapd callback object");
|
|
return createStatus(HostapdStatusCode::FAILURE_UNKNOWN);
|
|
}
|
|
callbacks_.push_back(callback);
|
|
return ndk::ScopedAStatus::ok();
|
|
}
|
|
|
|
::ndk::ScopedAStatus Hostapd::forceClientDisconnectInternal(const std::string& iface_name,
|
|
const std::vector<uint8_t>& client_address, Ieee80211ReasonCode reason_code)
|
|
{
|
|
struct hostapd_data *hapd = hostapd_get_iface(interfaces_, iface_name.c_str());
|
|
bool result;
|
|
if (!hapd) {
|
|
for (auto const& iface : br_interfaces_) {
|
|
if (iface.first == iface_name) {
|
|
for (auto const& instance : iface.second) {
|
|
hapd = hostapd_get_iface(interfaces_, instance.c_str());
|
|
if (hapd) {
|
|
result = forceStaDisconnection(hapd, client_address,
|
|
(uint16_t) reason_code);
|
|
if (result) break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
result = forceStaDisconnection(hapd, client_address, (uint16_t) reason_code);
|
|
}
|
|
if (!hapd) {
|
|
wpa_printf(MSG_ERROR, "Interface %s doesn't exist", iface_name.c_str());
|
|
return createStatus(HostapdStatusCode::FAILURE_IFACE_UNKNOWN);
|
|
}
|
|
if (result) {
|
|
return ndk::ScopedAStatus::ok();
|
|
}
|
|
return createStatus(HostapdStatusCode::FAILURE_CLIENT_UNKNOWN);
|
|
}
|
|
|
|
::ndk::ScopedAStatus Hostapd::setDebugParamsInternal(DebugLevel level)
|
|
{
|
|
wpa_debug_level = static_cast<uint32_t>(level);
|
|
return ndk::ScopedAStatus::ok();
|
|
}
|
|
|
|
} // namespace hostapd
|
|
} // namespace wifi
|
|
} // namespace hardware
|
|
} // namespace android
|
|
} // namespace aidl
|