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.
576 lines
19 KiB
576 lines
19 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 <string> |
|
#include <vector> |
|
|
|
#include <android-base/logging.h> |
|
|
|
#include "wificond/client_interface_impl.h" |
|
#include "wificond/scanning/offload/offload_scan_manager.h" |
|
#include "wificond/scanning/offload/offload_service_utils.h" |
|
#include "wificond/scanning/scan_utils.h" |
|
|
|
using android::binder::Status; |
|
using android::net::wifi::IPnoScanEvent; |
|
using android::net::wifi::IScanEvent; |
|
using android::hardware::wifi::offload::V1_0::IOffload; |
|
using android::sp; |
|
using com::android::server::wifi::wificond::NativeScanResult; |
|
using com::android::server::wifi::wificond::PnoSettings; |
|
using com::android::server::wifi::wificond::SingleScanSettings; |
|
|
|
using std::pair; |
|
using std::string; |
|
using std::vector; |
|
using std::weak_ptr; |
|
using std::shared_ptr; |
|
|
|
using namespace std::placeholders; |
|
|
|
namespace android { |
|
namespace wificond { |
|
|
|
ScannerImpl::ScannerImpl(uint32_t wiphy_index, uint32_t interface_index, |
|
const ScanCapabilities& scan_capabilities, |
|
const WiphyFeatures& wiphy_features, |
|
ClientInterfaceImpl* client_interface, |
|
NetlinkUtils* netlink_utils, ScanUtils* scan_utils, |
|
weak_ptr<OffloadServiceUtils> offload_service_utils) |
|
: valid_(true), |
|
scan_started_(false), |
|
pno_scan_started_(false), |
|
offload_scan_supported_(false), |
|
pno_scan_running_over_offload_(false), |
|
pno_scan_results_from_offload_(false), |
|
wiphy_index_(wiphy_index), |
|
interface_index_(interface_index), |
|
scan_capabilities_(scan_capabilities), |
|
wiphy_features_(wiphy_features), |
|
client_interface_(client_interface), |
|
netlink_utils_(netlink_utils), |
|
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)); |
|
std::shared_ptr<OffloadScanCallbackInterfaceImpl> |
|
offload_scan_callback_interface = |
|
offload_service_utils.lock()->GetOffloadScanCallbackInterface(this); |
|
offload_scan_manager_ = offload_service_utils.lock()->GetOffloadScanManager( |
|
offload_service_utils, offload_scan_callback_interface); |
|
offload_scan_supported_ = offload_service_utils.lock()->IsOffloadScanSupported(); |
|
} |
|
|
|
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_); |
|
} |
|
|
|
bool ScannerImpl::CheckIsValid() { |
|
if (!valid_) { |
|
LOG(DEBUG) << "Calling on a invalid scanner object." |
|
<< "Underlying client interface object was destroyed."; |
|
} |
|
return valid_; |
|
} |
|
|
|
Status ScannerImpl::getAvailable2gChannels( |
|
std::unique_ptr<vector<int32_t>>* out_frequencies) { |
|
if (!CheckIsValid()) { |
|
return Status::ok(); |
|
} |
|
BandInfo band_info; |
|
if (!netlink_utils_->GetWiphyInfo(wiphy_index_, &band_info, |
|
&scan_capabilities_, &wiphy_features_)) { |
|
LOG(ERROR) << "Failed to get wiphy info from kernel"; |
|
out_frequencies->reset(nullptr); |
|
return Status::ok(); |
|
} |
|
|
|
out_frequencies->reset( |
|
new vector<int32_t>(band_info.band_2g.begin(), band_info.band_2g.end())); |
|
return Status::ok(); |
|
} |
|
|
|
Status ScannerImpl::getAvailable5gNonDFSChannels( |
|
std::unique_ptr<vector<int32_t>>* out_frequencies) { |
|
if (!CheckIsValid()) { |
|
return Status::ok(); |
|
} |
|
BandInfo band_info; |
|
if (!netlink_utils_->GetWiphyInfo(wiphy_index_, &band_info, |
|
&scan_capabilities_, &wiphy_features_)) { |
|
LOG(ERROR) << "Failed to get wiphy info from kernel"; |
|
out_frequencies->reset(nullptr); |
|
return Status::ok(); |
|
} |
|
|
|
out_frequencies->reset( |
|
new vector<int32_t>(band_info.band_5g.begin(), band_info.band_5g.end())); |
|
return Status::ok(); |
|
} |
|
|
|
Status ScannerImpl::getAvailableDFSChannels( |
|
std::unique_ptr<vector<int32_t>>* out_frequencies) { |
|
if (!CheckIsValid()) { |
|
return Status::ok(); |
|
} |
|
BandInfo band_info; |
|
if (!netlink_utils_->GetWiphyInfo(wiphy_index_, &band_info, |
|
&scan_capabilities_, &wiphy_features_)) { |
|
LOG(ERROR) << "Failed to get wiphy info from kernel"; |
|
out_frequencies->reset(nullptr); |
|
return Status::ok(); |
|
} |
|
|
|
out_frequencies->reset(new vector<int32_t>(band_info.band_dfs.begin(), |
|
band_info.band_dfs.end())); |
|
return Status::ok(); |
|
} |
|
|
|
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 (pno_scan_results_from_offload_) { |
|
if (!offload_scan_manager_->getScanResults(out_scan_results)) { |
|
LOG(ERROR) << "Failed to get scan results via Offload HAL"; |
|
} |
|
} else { |
|
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(); |
|
|
|
// Initialize it with an empty ssid for a wild card scan. |
|
vector<vector<uint8_t>> ssids = {{}}; |
|
|
|
vector<vector<uint8_t>> skipped_scan_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; |
|
} |
|
ssids.push_back(network.ssid_); |
|
} |
|
|
|
LogSsidList(skipped_scan_ssids, "Skip scan ssid for single scan"); |
|
|
|
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, ssids, freqs, |
|
&error_code)) { |
|
CHECK(error_code != ENODEV) << "Driver is in a bad state, restarting wificond"; |
|
*out_success = false; |
|
return Status::ok(); |
|
} |
|
scan_started_ = true; |
|
*out_success = true; |
|
return Status::ok(); |
|
} |
|
|
|
Status ScannerImpl::startPnoScan(const PnoSettings& pno_settings, |
|
bool* out_success) { |
|
pno_settings_ = pno_settings; |
|
pno_scan_results_from_offload_ = false; |
|
LOG(VERBOSE) << "startPnoScan"; |
|
if (offload_scan_supported_ && StartPnoScanOffload(pno_settings)) { |
|
// scanning over offload succeeded |
|
*out_success = true; |
|
} else { |
|
*out_success = StartPnoScanDefault(pno_settings); |
|
} |
|
return Status::ok(); |
|
} |
|
|
|
bool ScannerImpl::StartPnoScanOffload(const PnoSettings& pno_settings) { |
|
OffloadScanManager::ReasonCode reason_code; |
|
vector<vector<uint8_t>> scan_ssids; |
|
vector<vector<uint8_t>> match_ssids; |
|
vector<uint8_t> match_security; |
|
// Empty frequency list: scan all frequencies. |
|
vector<uint32_t> freqs; |
|
|
|
ParsePnoSettings(pno_settings, &scan_ssids, &match_ssids, &freqs, |
|
&match_security); |
|
pno_scan_running_over_offload_ = offload_scan_manager_->startScan( |
|
pno_settings.interval_ms_, |
|
// TODO: honor both rssi thresholds. |
|
pno_settings.min_5g_rssi_, scan_ssids, match_ssids, match_security, freqs, |
|
&reason_code); |
|
if (pno_scan_running_over_offload_) { |
|
LOG(VERBOSE) << "Pno scans requested over Offload HAL"; |
|
if (pno_scan_event_handler_ != nullptr) { |
|
pno_scan_event_handler_->OnPnoScanOverOffloadStarted(); |
|
} |
|
} |
|
return pno_scan_running_over_offload_; |
|
} |
|
|
|
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; |
|
for (auto& network : pno_settings.pno_networks_) { |
|
// Add hidden network ssid. |
|
if (network.is_hidden_) { |
|
// TODO remove pruning for Offload Scans |
|
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); |
|
} |
|
|
|
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(); |
|
|
|
int error_code = 0; |
|
if (!scan_utils_->StartScheduledScan(interface_index_, |
|
GenerateIntervalSetting(pno_settings), |
|
// TODO: honor both rssi thresholds. |
|
pno_settings.min_5g_rssi_, |
|
request_random_mac, |
|
scan_ssids, |
|
match_ssids, |
|
freqs, |
|
&error_code)) { |
|
LOG(ERROR) << "Failed to start pno scan"; |
|
CHECK(error_code != ENODEV) << "Driver is in a bad state, restarting wificond"; |
|
return false; |
|
} |
|
LOG(INFO) << "Pno scan started"; |
|
pno_scan_started_ = true; |
|
return true; |
|
} |
|
|
|
Status ScannerImpl::stopPnoScan(bool* out_success) { |
|
if (offload_scan_supported_ && StopPnoScanOffload()) { |
|
// Pno scans over offload stopped successfully |
|
*out_success = true; |
|
} else { |
|
// Pno scans were not requested over offload |
|
*out_success = StopPnoScanDefault(); |
|
} |
|
return Status::ok(); |
|
} |
|
|
|
bool ScannerImpl::StopPnoScanOffload() { |
|
OffloadScanManager::ReasonCode reason_code; |
|
if (!pno_scan_running_over_offload_) { |
|
return false; |
|
} |
|
if (!offload_scan_manager_->stopScan(&reason_code)) { |
|
LOG(WARNING) << "Unable to unsubscribe to Offload scan results"; |
|
} |
|
pno_scan_running_over_offload_ = false; |
|
LOG(VERBOSE) << "Pno scans over Offload stopped"; |
|
return true; |
|
} |
|
|
|
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_results_from_offload_ = false; |
|
pno_scan_event_handler_->OnPnoNetworkFound(); |
|
} |
|
} |
|
} |
|
|
|
SchedScanIntervalSetting ScannerImpl::GenerateIntervalSetting( |
|
const ::com::android::server::wifi::wificond::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::OnOffloadScanResult() { |
|
if (!pno_scan_running_over_offload_) { |
|
LOG(WARNING) << "Scan results from Offload HAL but scan not requested over " |
|
"this interface"; |
|
return; |
|
} |
|
LOG(INFO) << "Offload Scan results received"; |
|
pno_scan_results_from_offload_ = true; |
|
if (pno_scan_event_handler_ != nullptr) { |
|
pno_scan_event_handler_->OnPnoNetworkFound(); |
|
} else { |
|
LOG(WARNING) << "No scan event handler Offload Scan result"; |
|
} |
|
} |
|
|
|
void ScannerImpl::OnOffloadError( |
|
OffloadScanCallbackInterface::AsyncErrorReason error_code) { |
|
if (!pno_scan_running_over_offload_) { |
|
// Ignore irrelevant error notifications |
|
LOG(WARNING) << "Offload HAL Async Error occured but Offload HAL is not " |
|
"subscribed to"; |
|
return; |
|
} |
|
LOG(ERROR) << "Offload Service Async Failure error_code=" << error_code; |
|
switch (error_code) { |
|
case OffloadScanCallbackInterface::AsyncErrorReason::BINDER_DEATH: |
|
LOG(ERROR) << "Binder death"; |
|
if (pno_scan_event_handler_ != nullptr) { |
|
pno_scan_event_handler_->OnPnoScanOverOffloadFailed( |
|
net::wifi::IPnoScanEvent::PNO_SCAN_OVER_OFFLOAD_BINDER_FAILURE); |
|
} |
|
break; |
|
case OffloadScanCallbackInterface::AsyncErrorReason::REMOTE_FAILURE: |
|
LOG(ERROR) << "Remote failure"; |
|
if (pno_scan_event_handler_ != nullptr) { |
|
pno_scan_event_handler_->OnPnoScanOverOffloadFailed( |
|
net::wifi::IPnoScanEvent::PNO_SCAN_OVER_OFFLOAD_REMOTE_FAILURE); |
|
} |
|
break; |
|
default: |
|
LOG(WARNING) << "Invalid Error code"; |
|
break; |
|
} |
|
bool success = false; |
|
// Stop scans over Offload HAL and request them over netlink |
|
stopPnoScan(&success); |
|
if (success) { |
|
LOG(INFO) << "Pno scans stopped"; |
|
} |
|
// Restart PNO scans over netlink interface |
|
success = StartPnoScanDefault(pno_settings_); |
|
if (success) { |
|
LOG(INFO) << "Pno scans restarted"; |
|
} else { |
|
LOG(ERROR) << "Unable to fall back to netlink pno scan"; |
|
pno_scan_event_handler_->OnPnoScanFailed(); |
|
} |
|
} |
|
|
|
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
|
|
|