You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
491 lines
16 KiB
491 lines
16 KiB
/*
|
|
* Copyright (C) 2016 The Android Open Source Project
|
|
*
|
|
* 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 "wificond/scanning/scanner_impl.h"
|
|
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <android-base/logging.h>
|
|
|
|
#include "wificond/client_interface_impl.h"
|
|
#include "wificond/scanning/scan_utils.h"
|
|
|
|
using android::binder::Status;
|
|
using android::sp;
|
|
using android::net::wifi::nl80211::IPnoScanEvent;
|
|
using android::net::wifi::nl80211::IScanEvent;
|
|
using android::net::wifi::nl80211::IWifiScannerImpl;
|
|
using android::net::wifi::nl80211::NativeScanResult;
|
|
using android::net::wifi::nl80211::PnoSettings;
|
|
using android::net::wifi::nl80211::SingleScanSettings;
|
|
|
|
using std::string;
|
|
using std::vector;
|
|
using std::weak_ptr;
|
|
using std::shared_ptr;
|
|
|
|
using namespace std::placeholders;
|
|
|
|
namespace {
|
|
using android::wificond::WiphyFeatures;
|
|
bool IsScanTypeSupported(int scan_type, const WiphyFeatures& wiphy_features) {
|
|
switch(scan_type) {
|
|
case IWifiScannerImpl::SCAN_TYPE_LOW_SPAN:
|
|
return wiphy_features.supports_low_span_oneshot_scan;
|
|
case IWifiScannerImpl::SCAN_TYPE_LOW_POWER:
|
|
return wiphy_features.supports_low_power_oneshot_scan;
|
|
case IWifiScannerImpl::SCAN_TYPE_HIGH_ACCURACY:
|
|
return wiphy_features.supports_high_accuracy_oneshot_scan;
|
|
default:
|
|
CHECK(0) << "Invalid scan type received: " << scan_type;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
constexpr const int kPercentNetworksWithFreq = 30;
|
|
constexpr const int32_t kPnoScanDefaultFreqs2G[] = {2412, 2417, 2422, 2427, 2432, 2437, 2447, 2452,
|
|
2457, 2462};
|
|
constexpr const int32_t kPnoScanDefaultFreqs5G[] = {5180, 5200, 5220, 5240, 5745, 5765, 5785, 5805};
|
|
} // namespace
|
|
|
|
namespace android {
|
|
namespace wificond {
|
|
|
|
ScannerImpl::ScannerImpl(uint32_t interface_index,
|
|
const ScanCapabilities& scan_capabilities,
|
|
const WiphyFeatures& wiphy_features,
|
|
ClientInterfaceImpl* client_interface,
|
|
ScanUtils* scan_utils)
|
|
: valid_(true),
|
|
scan_started_(false),
|
|
pno_scan_started_(false),
|
|
nodev_counter_(0),
|
|
interface_index_(interface_index),
|
|
scan_capabilities_(scan_capabilities),
|
|
wiphy_features_(wiphy_features),
|
|
client_interface_(client_interface),
|
|
scan_utils_(scan_utils),
|
|
scan_event_handler_(nullptr) {
|
|
// Subscribe one-shot scan result notification from kernel.
|
|
LOG(INFO) << "subscribe scan result for interface with index: "
|
|
<< (int)interface_index_;
|
|
scan_utils_->SubscribeScanResultNotification(
|
|
interface_index_,
|
|
std::bind(&ScannerImpl::OnScanResultsReady, this, _1, _2, _3, _4));
|
|
// Subscribe scheduled scan result notification from kernel.
|
|
scan_utils_->SubscribeSchedScanResultNotification(
|
|
interface_index_,
|
|
std::bind(&ScannerImpl::OnSchedScanResultsReady,
|
|
this,
|
|
_1, _2));
|
|
}
|
|
|
|
ScannerImpl::~ScannerImpl() {}
|
|
|
|
void ScannerImpl::Invalidate() {
|
|
LOG(INFO) << "Unsubscribe scan result for interface with index: "
|
|
<< (int)interface_index_;
|
|
scan_utils_->UnsubscribeScanResultNotification(interface_index_);
|
|
scan_utils_->UnsubscribeSchedScanResultNotification(interface_index_);
|
|
valid_ = false;
|
|
}
|
|
|
|
bool ScannerImpl::CheckIsValid() {
|
|
if (!valid_) {
|
|
LOG(DEBUG) << "Calling on a invalid scanner object."
|
|
<< "Underlying client interface object was destroyed.";
|
|
}
|
|
return valid_;
|
|
}
|
|
|
|
Status ScannerImpl::getScanResults(vector<NativeScanResult>* out_scan_results) {
|
|
if (!CheckIsValid()) {
|
|
return Status::ok();
|
|
}
|
|
if (!scan_utils_->GetScanResult(interface_index_, out_scan_results)) {
|
|
LOG(ERROR) << "Failed to get scan results via NL80211";
|
|
}
|
|
return Status::ok();
|
|
}
|
|
|
|
Status ScannerImpl::getPnoScanResults(
|
|
vector<NativeScanResult>* out_scan_results) {
|
|
if (!CheckIsValid()) {
|
|
return Status::ok();
|
|
}
|
|
if (!scan_utils_->GetScanResult(interface_index_, out_scan_results)) {
|
|
LOG(ERROR) << "Failed to get scan results via NL80211";
|
|
}
|
|
return Status::ok();
|
|
}
|
|
|
|
Status ScannerImpl::scan(const SingleScanSettings& scan_settings,
|
|
bool* out_success) {
|
|
if (!CheckIsValid()) {
|
|
*out_success = false;
|
|
return Status::ok();
|
|
}
|
|
|
|
if (scan_started_) {
|
|
LOG(WARNING) << "Scan already started";
|
|
}
|
|
// Only request MAC address randomization when station is not associated.
|
|
bool request_random_mac =
|
|
wiphy_features_.supports_random_mac_oneshot_scan &&
|
|
!client_interface_->IsAssociated();
|
|
int scan_type = scan_settings.scan_type_;
|
|
if (!IsScanTypeSupported(scan_settings.scan_type_, wiphy_features_)) {
|
|
LOG(DEBUG) << "Ignoring scan type because device does not support it";
|
|
scan_type = SCAN_TYPE_DEFAULT;
|
|
}
|
|
|
|
// Initialize it with an empty ssid for a wild card scan.
|
|
vector<vector<uint8_t>> ssids = {{}};
|
|
|
|
vector<vector<uint8_t>> skipped_scan_ssids;
|
|
vector<vector<uint8_t>> skipped_long_ssids;
|
|
for (auto& network : scan_settings.hidden_networks_) {
|
|
if (ssids.size() + 1 > scan_capabilities_.max_num_scan_ssids) {
|
|
skipped_scan_ssids.emplace_back(network.ssid_);
|
|
continue;
|
|
}
|
|
if (network.ssid_.size() > 32) {
|
|
skipped_long_ssids.emplace_back(network.ssid_);
|
|
continue;
|
|
}
|
|
ssids.push_back(network.ssid_);
|
|
}
|
|
|
|
LogSsidList(skipped_scan_ssids, "Skip scan ssid for single scan");
|
|
LogSsidList(skipped_long_ssids, "Skip too long ssid");
|
|
|
|
vector<uint32_t> freqs;
|
|
for (auto& channel : scan_settings.channel_settings_) {
|
|
freqs.push_back(channel.frequency_);
|
|
}
|
|
|
|
int error_code = 0;
|
|
if (!scan_utils_->Scan(interface_index_, request_random_mac, scan_type,
|
|
scan_settings.enable_6ghz_rnr_, ssids, freqs, &error_code)) {
|
|
if (error_code == ENODEV) {
|
|
nodev_counter_ ++;
|
|
LOG(WARNING) << "Scan failed with error=nodev. counter=" << nodev_counter_;
|
|
}
|
|
CHECK(error_code != ENODEV || nodev_counter_ <= 3)
|
|
<< "Driver is in a bad state, restarting wificond";
|
|
*out_success = false;
|
|
return Status::ok();
|
|
}
|
|
nodev_counter_ = 0;
|
|
scan_started_ = true;
|
|
*out_success = true;
|
|
return Status::ok();
|
|
}
|
|
|
|
Status ScannerImpl::startPnoScan(const PnoSettings& pno_settings,
|
|
bool* out_success) {
|
|
pno_settings_ = pno_settings;
|
|
LOG(VERBOSE) << "startPnoScan";
|
|
*out_success = StartPnoScanDefault(pno_settings);
|
|
return Status::ok();
|
|
}
|
|
|
|
void ScannerImpl::ParsePnoSettings(const PnoSettings& pno_settings,
|
|
vector<vector<uint8_t>>* scan_ssids,
|
|
vector<vector<uint8_t>>* match_ssids,
|
|
vector<uint32_t>* freqs,
|
|
vector<uint8_t>* match_security) {
|
|
// TODO provide actionable security match parameters
|
|
const uint8_t kNetworkFlagsDefault = 0;
|
|
vector<vector<uint8_t>> skipped_scan_ssids;
|
|
vector<vector<uint8_t>> skipped_match_ssids;
|
|
std::set<int32_t> unique_frequencies;
|
|
int num_networks_no_freqs = 0;
|
|
for (auto& network : pno_settings.pno_networks_) {
|
|
// Add hidden network ssid.
|
|
if (network.is_hidden_) {
|
|
if (scan_ssids->size() + 1 >
|
|
scan_capabilities_.max_num_sched_scan_ssids) {
|
|
skipped_scan_ssids.emplace_back(network.ssid_);
|
|
continue;
|
|
}
|
|
scan_ssids->push_back(network.ssid_);
|
|
}
|
|
|
|
if (match_ssids->size() + 1 > scan_capabilities_.max_match_sets) {
|
|
skipped_match_ssids.emplace_back(network.ssid_);
|
|
continue;
|
|
}
|
|
match_ssids->push_back(network.ssid_);
|
|
match_security->push_back(kNetworkFlagsDefault);
|
|
|
|
// build the set of unique frequencies to scan for.
|
|
for (const auto& frequency : network.frequencies_) {
|
|
unique_frequencies.insert(frequency);
|
|
}
|
|
if (network.frequencies_.empty()) {
|
|
num_networks_no_freqs++;
|
|
}
|
|
}
|
|
|
|
// Also scan the default frequencies if there is frequency data passed down but more than 30% of
|
|
// networks don't have frequency data.
|
|
if (unique_frequencies.size() > 0 && num_networks_no_freqs * 100 / match_ssids->size()
|
|
> kPercentNetworksWithFreq) {
|
|
// Filter out frequencies not supported by chip.
|
|
const auto band_2g = client_interface_->GetBandInfo().band_2g;
|
|
for (const auto frequency : kPnoScanDefaultFreqs2G) {
|
|
if (std::find(band_2g.begin(), band_2g.end(), frequency) != band_2g.end()) {
|
|
unique_frequencies.insert(frequency);
|
|
}
|
|
}
|
|
// Note: kPnoScanDefaultFreqs5G doesn't contain DFS frequencies.
|
|
const auto band_5g = client_interface_->GetBandInfo().band_5g;
|
|
for (const auto frequency : kPnoScanDefaultFreqs5G) {
|
|
if (std::find(band_5g.begin(), band_5g.end(), frequency) != band_5g.end()) {
|
|
unique_frequencies.insert(frequency);
|
|
}
|
|
}
|
|
}
|
|
for (const auto& frequency : unique_frequencies) {
|
|
freqs->push_back(frequency);
|
|
}
|
|
LogSsidList(skipped_scan_ssids, "Skip scan ssid for pno scan");
|
|
LogSsidList(skipped_match_ssids, "Skip match ssid for pno scan");
|
|
}
|
|
|
|
bool ScannerImpl::StartPnoScanDefault(const PnoSettings& pno_settings) {
|
|
if (!CheckIsValid()) {
|
|
return false;
|
|
}
|
|
if (pno_scan_started_) {
|
|
LOG(WARNING) << "Pno scan already started";
|
|
}
|
|
// An empty ssid for a wild card scan.
|
|
vector<vector<uint8_t>> scan_ssids = {{}};
|
|
vector<vector<uint8_t>> match_ssids;
|
|
vector<uint8_t> unused;
|
|
// Empty frequency list: scan all frequencies.
|
|
vector<uint32_t> freqs;
|
|
|
|
ParsePnoSettings(pno_settings, &scan_ssids, &match_ssids, &freqs, &unused);
|
|
// Only request MAC address randomization when station is not associated.
|
|
bool request_random_mac = wiphy_features_.supports_random_mac_sched_scan &&
|
|
!client_interface_->IsAssociated();
|
|
// Always request a low power scan for PNO, if device supports it.
|
|
bool request_low_power = wiphy_features_.supports_low_power_oneshot_scan;
|
|
|
|
bool request_sched_scan_relative_rssi = wiphy_features_.supports_ext_sched_scan_relative_rssi;
|
|
|
|
int error_code = 0;
|
|
struct SchedScanReqFlags req_flags = {};
|
|
req_flags.request_random_mac = request_random_mac;
|
|
req_flags.request_low_power = request_low_power;
|
|
req_flags.request_sched_scan_relative_rssi = request_sched_scan_relative_rssi;
|
|
if (!scan_utils_->StartScheduledScan(interface_index_,
|
|
GenerateIntervalSetting(pno_settings),
|
|
pno_settings.min_2g_rssi_,
|
|
pno_settings.min_5g_rssi_,
|
|
pno_settings.min_6g_rssi_,
|
|
req_flags,
|
|
scan_ssids,
|
|
match_ssids,
|
|
freqs,
|
|
&error_code)) {
|
|
if (error_code == ENODEV) {
|
|
nodev_counter_ ++;
|
|
LOG(WARNING) << "Pno Scan failed with error=nodev. counter=" << nodev_counter_;
|
|
}
|
|
LOG(ERROR) << "Failed to start pno scan";
|
|
CHECK(error_code != ENODEV || nodev_counter_ <= 3)
|
|
<< "Driver is in a bad state, restarting wificond";
|
|
return false;
|
|
}
|
|
string freq_string;
|
|
if (freqs.empty()) {
|
|
freq_string = "for all supported frequencies";
|
|
} else {
|
|
freq_string = "for frequencies: ";
|
|
for (uint32_t f : freqs) {
|
|
freq_string += std::to_string(f) + ", ";
|
|
}
|
|
}
|
|
LOG(INFO) << "Pno scan started " << freq_string;
|
|
nodev_counter_ = 0;
|
|
pno_scan_started_ = true;
|
|
return true;
|
|
}
|
|
|
|
Status ScannerImpl::stopPnoScan(bool* out_success) {
|
|
*out_success = StopPnoScanDefault();
|
|
return Status::ok();
|
|
}
|
|
|
|
bool ScannerImpl::StopPnoScanDefault() {
|
|
if (!CheckIsValid()) {
|
|
return false;
|
|
}
|
|
|
|
if (!pno_scan_started_) {
|
|
LOG(WARNING) << "No pno scan started";
|
|
}
|
|
if (!scan_utils_->StopScheduledScan(interface_index_)) {
|
|
return false;
|
|
}
|
|
LOG(INFO) << "Pno scan stopped";
|
|
pno_scan_started_ = false;
|
|
return true;
|
|
}
|
|
|
|
Status ScannerImpl::abortScan() {
|
|
if (!CheckIsValid()) {
|
|
return Status::ok();
|
|
}
|
|
|
|
if (!scan_started_) {
|
|
LOG(WARNING) << "Scan is not started. Ignore abort request";
|
|
return Status::ok();
|
|
}
|
|
if (!scan_utils_->AbortScan(interface_index_)) {
|
|
LOG(WARNING) << "Abort scan failed";
|
|
}
|
|
return Status::ok();
|
|
}
|
|
|
|
Status ScannerImpl::subscribeScanEvents(const sp<IScanEvent>& handler) {
|
|
if (!CheckIsValid()) {
|
|
return Status::ok();
|
|
}
|
|
|
|
if (scan_event_handler_ != nullptr) {
|
|
LOG(ERROR) << "Found existing scan events subscriber."
|
|
<< " This subscription request will unsubscribe it";
|
|
}
|
|
scan_event_handler_ = handler;
|
|
return Status::ok();
|
|
}
|
|
|
|
Status ScannerImpl::unsubscribeScanEvents() {
|
|
scan_event_handler_ = nullptr;
|
|
return Status::ok();
|
|
}
|
|
|
|
Status ScannerImpl::subscribePnoScanEvents(const sp<IPnoScanEvent>& handler) {
|
|
if (!CheckIsValid()) {
|
|
return Status::ok();
|
|
}
|
|
|
|
if (pno_scan_event_handler_ != nullptr) {
|
|
LOG(ERROR) << "Found existing pno scan events subscriber."
|
|
<< " This subscription request will unsubscribe it";
|
|
}
|
|
pno_scan_event_handler_ = handler;
|
|
|
|
return Status::ok();
|
|
}
|
|
|
|
Status ScannerImpl::unsubscribePnoScanEvents() {
|
|
pno_scan_event_handler_ = nullptr;
|
|
return Status::ok();
|
|
}
|
|
|
|
void ScannerImpl::OnScanResultsReady(uint32_t interface_index, bool aborted,
|
|
vector<vector<uint8_t>>& ssids,
|
|
vector<uint32_t>& frequencies) {
|
|
if (!scan_started_) {
|
|
LOG(INFO) << "Received external scan result notification from kernel.";
|
|
}
|
|
scan_started_ = false;
|
|
if (scan_event_handler_ != nullptr) {
|
|
// TODO: Pass other parameters back once we find framework needs them.
|
|
if (aborted) {
|
|
LOG(WARNING) << "Scan aborted";
|
|
scan_event_handler_->OnScanFailed();
|
|
} else {
|
|
scan_event_handler_->OnScanResultReady();
|
|
}
|
|
} else {
|
|
LOG(WARNING) << "No scan event handler found.";
|
|
}
|
|
}
|
|
|
|
void ScannerImpl::OnSchedScanResultsReady(uint32_t interface_index,
|
|
bool scan_stopped) {
|
|
if (pno_scan_event_handler_ != nullptr) {
|
|
if (scan_stopped) {
|
|
// If |pno_scan_started_| is false.
|
|
// This stop notification might result from our own request.
|
|
// See the document for NL80211_CMD_SCHED_SCAN_STOPPED in nl80211.h.
|
|
if (pno_scan_started_) {
|
|
LOG(WARNING) << "Unexpected pno scan stopped event";
|
|
pno_scan_event_handler_->OnPnoScanFailed();
|
|
}
|
|
pno_scan_started_ = false;
|
|
} else {
|
|
LOG(INFO) << "Pno scan result ready event";
|
|
pno_scan_event_handler_->OnPnoNetworkFound();
|
|
}
|
|
}
|
|
}
|
|
|
|
SchedScanIntervalSetting ScannerImpl::GenerateIntervalSetting(
|
|
const android::net::wifi::nl80211::PnoSettings&
|
|
pno_settings) const {
|
|
bool support_num_scan_plans = scan_capabilities_.max_num_scan_plans >= 2;
|
|
bool support_scan_plan_interval =
|
|
scan_capabilities_.max_scan_plan_interval * 1000 >=
|
|
pno_settings.interval_ms_ * PnoSettings::kSlowScanIntervalMultiplier;
|
|
bool support_scan_plan_iterations =
|
|
scan_capabilities_.max_scan_plan_iterations >=
|
|
PnoSettings::kFastScanIterations;
|
|
|
|
uint32_t fast_scan_interval =
|
|
static_cast<uint32_t>(pno_settings.interval_ms_);
|
|
if (support_num_scan_plans && support_scan_plan_interval &&
|
|
support_scan_plan_iterations) {
|
|
return SchedScanIntervalSetting{
|
|
{{fast_scan_interval, PnoSettings::kFastScanIterations}},
|
|
fast_scan_interval * PnoSettings::kSlowScanIntervalMultiplier};
|
|
} else {
|
|
// Device doesn't support the provided scan plans.
|
|
// Specify single interval instead.
|
|
// In this case, the driver/firmware is expected to implement back off
|
|
// logic internally using |pno_settings.interval_ms_| as "fast scan"
|
|
// interval.
|
|
return SchedScanIntervalSetting{{}, fast_scan_interval};
|
|
}
|
|
}
|
|
|
|
void ScannerImpl::LogSsidList(vector<vector<uint8_t>>& ssid_list,
|
|
string prefix) {
|
|
if (ssid_list.empty()) {
|
|
return;
|
|
}
|
|
string ssid_list_string;
|
|
for (auto& ssid : ssid_list) {
|
|
ssid_list_string += string(ssid.begin(), ssid.end());
|
|
if (&ssid != &ssid_list.back()) {
|
|
ssid_list_string += ", ";
|
|
}
|
|
}
|
|
LOG(WARNING) << prefix << ": " << ssid_list_string;
|
|
}
|
|
|
|
} // namespace wificond
|
|
} // namespace android
|