/*
* 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/scan_utils.h"
#include "wificond/scanning/offload/offload_service_utils.h"
#include "wificond/scanning/offload/offload_scan_manager.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 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),
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));
offload_scan_manager_.reset(
new OffloadScanManager(
offload_service_utils,
std::bind(&ScannerImpl::OnOffloadScanResult,
this, _1)));
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::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) {
if (!offload_scan_supported_ || !StartPnoScanOffload(pno_settings)) {
*out_success = StartPnoScanDefault(pno_settings);
} else {
// scanning over offload succeeded
*out_success = true;
}
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);
bool success = 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);
return success;
}
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()) {
*out_success = StopPnoScanDefault();
} else {
// Pno scans over offload stopped successfully
*out_success = true;
}
return Status::ok();
}
bool ScannerImpl::StopPnoScanOffload() {
OffloadScanManager::ReasonCode reason_code;
return(offload_scan_manager_->stopScan(&reason_code));
}
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 ::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::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;
}
void ScannerImpl::OnOffloadScanResult(
std::vector<NativeScanResult> scanResult) {
LOG(INFO) << "Offload Scan results received";
if (pno_scan_event_handler_ != nullptr) {
pno_scan_event_handler_->OnPnoNetworkFound();
} else {
LOG(WARNING) << "No scan event handler Offload Scan result";
}
}
} // namespace wificond
} // namespace android