| /* |
| * 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 |