/* * 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/client_interface_impl.h" #include #include #include #include "wificond/client_interface_binder.h" #include "wificond/logging_utils.h" #include "wificond/net/mlme_event.h" #include "wificond/net/netlink_utils.h" #include "wificond/scanning/scan_result.h" #include "wificond/scanning/scan_utils.h" #include "wificond/scanning/scanner_impl.h" using android::net::wifi::nl80211::IClientInterface; using android::net::wifi::nl80211::ISendMgmtFrameEvent; using android::net::wifi::nl80211::NativeScanResult; using android::sp; using android::wifi_system::InterfaceTool; using std::endl; using std::string; using std::unique_ptr; using std::vector; using namespace std::placeholders; namespace android { namespace wificond { MlmeEventHandlerImpl::MlmeEventHandlerImpl(ClientInterfaceImpl* client_interface) : client_interface_(client_interface) { } MlmeEventHandlerImpl::~MlmeEventHandlerImpl() { } void MlmeEventHandlerImpl::OnConnect(unique_ptr event) { if (!event->IsTimeout() && event->GetStatusCode() == 0) { client_interface_->is_associated_ = true; client_interface_->RefreshAssociateFreq(); client_interface_->bssid_ = event->GetBSSID(); } else { if (event->IsTimeout()) { LOG(INFO) << "Connect timeout"; } client_interface_->is_associated_ = false; client_interface_->bssid_.fill(0); } } void MlmeEventHandlerImpl::OnRoam(unique_ptr event) { client_interface_->is_associated_ = true; client_interface_->RefreshAssociateFreq(); client_interface_->bssid_ = event->GetBSSID(); } void MlmeEventHandlerImpl::OnAssociate(unique_ptr event) { if (!event->IsTimeout() && event->GetStatusCode() == 0) { client_interface_->is_associated_ = true; client_interface_->RefreshAssociateFreq(); client_interface_->bssid_ = event->GetBSSID(); } else { if (event->IsTimeout()) { LOG(INFO) << "Associate timeout"; } client_interface_->is_associated_ = false; client_interface_->bssid_.fill(0); } } void MlmeEventHandlerImpl::OnDisconnect(unique_ptr event) { client_interface_->is_associated_ = false; client_interface_->bssid_.fill(0); } void MlmeEventHandlerImpl::OnDisassociate(unique_ptr event) { client_interface_->is_associated_ = false; client_interface_->bssid_.fill(0); } ClientInterfaceImpl::ClientInterfaceImpl( uint32_t wiphy_index, const std::string& interface_name, uint32_t interface_index, const std::array& interface_mac_addr, InterfaceTool* if_tool, NetlinkUtils* netlink_utils, ScanUtils* scan_utils) : wiphy_index_(wiphy_index), interface_name_(interface_name), interface_index_(interface_index), interface_mac_addr_(interface_mac_addr), if_tool_(if_tool), netlink_utils_(netlink_utils), scan_utils_(scan_utils), mlme_event_handler_(new MlmeEventHandlerImpl(this)), binder_(new ClientInterfaceBinder(this)), is_associated_(false), frame_tx_in_progress_(false), frame_tx_status_cookie_(0), on_frame_tx_status_event_handler_([](bool was_acked) {}) { netlink_utils_->SubscribeMlmeEvent( interface_index_, mlme_event_handler_.get()); netlink_utils_->SubscribeFrameTxStatusEvent( interface_index, [this](uint64_t cookie, bool was_acked) { if (frame_tx_in_progress_ && frame_tx_status_cookie_ == cookie) { on_frame_tx_status_event_handler_(was_acked); frame_tx_in_progress_ = false; frame_tx_status_cookie_ = 0; on_frame_tx_status_event_handler_ = [](bool was_acked) {}; } }); netlink_utils_->SubscribeChannelSwitchEvent(interface_index_, std::bind(&ClientInterfaceImpl::OnChannelSwitchEvent, this, _1)); if (!netlink_utils_->GetWiphyInfo(wiphy_index_, &band_info_, &scan_capabilities_, &wiphy_features_)) { LOG(ERROR) << "Failed to get wiphy info from kernel"; } LOG(INFO) << "create scanner for interface with index: " << (int)interface_index_; scanner_ = new ScannerImpl(interface_index_, scan_capabilities_, wiphy_features_, this, scan_utils_); // Need to set the interface up (especially in scan mode since wpa_supplicant // is not started) if_tool_->SetUpState(interface_name_.c_str(), true); } ClientInterfaceImpl::~ClientInterfaceImpl() { binder_->NotifyImplDead(); scanner_->Invalidate(); netlink_utils_->UnsubscribeFrameTxStatusEvent(interface_index_); netlink_utils_->UnsubscribeMlmeEvent(interface_index_); netlink_utils_->UnsubscribeChannelSwitchEvent(interface_index_); if_tool_->SetUpState(interface_name_.c_str(), false); } sp ClientInterfaceImpl::GetBinder() const { return binder_; } void ClientInterfaceImpl::Dump(std::stringstream* ss) const { *ss << "------- Dump of client interface with index: " << interface_index_ << " and name: " << interface_name_ << "-------" << endl; *ss << "Max number of ssids for single shot scan: " << static_cast(scan_capabilities_.max_num_scan_ssids) << endl; *ss << "Max number of ssids for scheduled scan: " << static_cast(scan_capabilities_.max_num_sched_scan_ssids) << endl; *ss << "Max number of match sets for scheduled scan: " << static_cast(scan_capabilities_.max_match_sets) << endl; *ss << "Maximum number of scan plans: " << scan_capabilities_.max_num_scan_plans << endl; *ss << "Max scan plan interval in seconds: " << scan_capabilities_.max_scan_plan_interval << endl; *ss << "Max scan plan iterations: " << scan_capabilities_.max_scan_plan_iterations << endl; *ss << "Device supports random MAC for single shot scan: " << wiphy_features_.supports_random_mac_oneshot_scan << endl; *ss << "Device supports low span single shot scan: " << wiphy_features_.supports_low_span_oneshot_scan << endl; *ss << "Device supports low power single shot scan: " << wiphy_features_.supports_low_power_oneshot_scan << endl; *ss << "Device supports high accuracy single shot scan: " << wiphy_features_.supports_high_accuracy_oneshot_scan << endl; *ss << "Device supports random MAC for scheduled scan: " << wiphy_features_.supports_random_mac_sched_scan << endl; *ss << "Device supports sending management frames at specified MCS rate: " << wiphy_features_.supports_tx_mgmt_frame_mcs << endl; *ss << "------- Dump End -------" << endl; } bool ClientInterfaceImpl::GetPacketCounters(vector* out_packet_counters) { StationInfo station_info; if (!netlink_utils_->GetStationInfo(interface_index_, bssid_, &station_info)) { return false; } out_packet_counters->push_back(station_info.station_tx_packets); out_packet_counters->push_back(station_info.station_tx_failed); return true; } bool ClientInterfaceImpl::SignalPoll(vector* out_signal_poll_results) { if (!IsAssociated()) { LOG(INFO) << "Fail RSSI polling because wifi is not associated."; return false; } StationInfo station_info; if (!netlink_utils_->GetStationInfo(interface_index_, bssid_, &station_info)) { return false; } out_signal_poll_results->push_back( static_cast(station_info.current_rssi)); // Convert from 100kbit/s to Mbps. out_signal_poll_results->push_back( static_cast(station_info.station_tx_bitrate/10)); // Association frequency. out_signal_poll_results->push_back( static_cast(associate_freq_)); // Convert from 100kbit/s to Mbps. out_signal_poll_results->push_back( static_cast(station_info.station_rx_bitrate/10)); return true; } const std::array& ClientInterfaceImpl::GetMacAddress() { return interface_mac_addr_; } void ClientInterfaceImpl::UpdateBandInfo() { LOG(INFO) << "UpdateBandInfo"; if (!netlink_utils_->GetWiphyInfo(wiphy_index_, &band_info_, &scan_capabilities_, &wiphy_features_)) { LOG(ERROR) << "Failed to get wiphy info from kernel"; } } const BandInfo& ClientInterfaceImpl::GetBandInfo() const { return band_info_; } bool ClientInterfaceImpl::RefreshAssociateFreq() { // wpa_supplicant fetches associate frequency using the latest scan result. // We should follow the same method here before we find a better solution. std::vector scan_results; if (!scan_utils_->GetScanResult(interface_index_, &scan_results)) { return false; } for (auto& scan_result : scan_results) { if (scan_result.associated) { associate_freq_ = scan_result.frequency; } } return false; } bool ClientInterfaceImpl::OnChannelSwitchEvent(uint32_t frequency) { if(!frequency) { LOG(ERROR) << "Frequency value is null"; return false; } LOG(INFO) << "New channel on frequency: " << frequency; associate_freq_ = frequency; return true; } bool ClientInterfaceImpl::IsAssociated() const { return is_associated_; } void ClientInterfaceImpl::SendMgmtFrame(const vector& frame, const sp& callback, int32_t mcs) { if (mcs >= 0 && !wiphy_features_.supports_tx_mgmt_frame_mcs) { callback->OnFailure( ISendMgmtFrameEvent::SEND_MGMT_FRAME_ERROR_MCS_UNSUPPORTED); return; } uint64_t cookie; if (!netlink_utils_->SendMgmtFrame(interface_index_, frame, mcs, &cookie)) { callback->OnFailure(ISendMgmtFrameEvent::SEND_MGMT_FRAME_ERROR_UNKNOWN); return; } frame_tx_in_progress_ = true; frame_tx_status_cookie_ = cookie; nsecs_t start_time_ns = systemTime(SYSTEM_TIME_MONOTONIC); on_frame_tx_status_event_handler_ = [callback, start_time_ns](bool was_acked) { if (was_acked) { nsecs_t end_time_ns = systemTime(SYSTEM_TIME_MONOTONIC); int32_t elapsed_time_ms = static_cast( nanoseconds_to_milliseconds(end_time_ns - start_time_ns)); callback->OnAck(elapsed_time_ms); } else { callback->OnFailure( ISendMgmtFrameEvent::SEND_MGMT_FRAME_ERROR_NO_ACK); } }; } } // namespace wificond } // namespace android