summaryrefslogblamecommitdiffstats
blob: 74b156c5f4a5ec409295637840daf4d49a24e7bb (plain) (tree)


























                                                                           
                         
 

                                   


                                  
                               

                            
                      






                    

                                                              
                                                
                                                           

                                          


                                                               

 


                                                     

                              


                           


                                   






                                                       
                                                         







                                                            








                                                                       

                                                                             
                                
                             




                                                                          
 
                                                            
 

                                                      
                                          

               
 





                                                                    

                                                       
                                                                
                                                      





                                                                   



                                   







                                                                             
                                      
                       
                                   
                                       
                    

                                   


   

                                                                          


                                                              
                                                   



                                                         
                                                                    



                                            
                                                                        


                                              
                                         





                                                             
                                                                        




                                                               

                        
                                                                            

                                                 
                                                                        






                                                                
                              



                                                      
                                                 




                                                               
                                             




                                                                
                                            


                            
                  


                                         




                                                              


                                                         
                                                       


                                                         
 
                  
              

 



                                        

                                                   
                                                                   










                                                                         
                                                
                                
                                                        








                                                             
                                                  
 




                                                                           

                                                                      





















                                                                     
                                      

                 


              


                                                     

















                                                                            




                                                           
                                                                      









                                                                             
                                                                   























                                                                        
 
                                                                               

                                                      
                                                                






                                                                        
                                                         




                                     
                    
                                                                      
                              




                                                                         
                                   






                                                                                           
                             







                                                                       
                                                         

                                                      
                        
                            
                                                                        
                     
                                                              




                 
                                        
                                                     

 




                                                       
                                                                            
                                               


                                                                        

                 
                                   
                                                                       





                                                    


















                                                                             

                                                                               


                                                          
                                          
 



                                                                              
                                          


           

                                                  
                                               












                                                                          


                                            


                                     



                                          






















                                                                         





































                                                                                
                                                                        





                                                                        








                                                                            
                                                             








                                                                
                                                   





                                                                  
                                                     





                                                             
                                                

           
   














                                                                     

 






                                                                                      
                                                                          





                                                                          
                                                                                    

 
                                                                                 
                    
                                                                    


                                                                                



                                                         
 
                                                                    
                                                       

                                                                            




                                  
                                                                    


                                                                               
          

                                                       

     

                                  
                                                                          


                                                                               
          

                                                       


                                          
                                                   

 









                                                                        









                                                                       








                                                           

 








                                                                     










                                                                 

                        
/*
 * 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 "net/netlink_manager.h"

#include <string>
#include <vector>

#include <linux/netlink.h>
#include <linux/nl80211.h>
#include <poll.h>
#include <sys/socket.h>

#include <android-base/logging.h>
#include <utils/Timers.h>

#include "net/mlme_event.h"
#include "net/mlme_event_handler.h"
#include "net/nl80211_attribute.h"
#include "net/nl80211_packet.h"

using android::base::unique_fd;
using std::placeholders::_1;
using std::string;
using std::unique_ptr;
using std::vector;

namespace android {
namespace wificond {

namespace {

// netlink.h suggests NLMSG_GOODSIZE to be at most 8192 bytes.
constexpr int kReceiveBufferSize = 8 * 1024;
constexpr uint32_t kBroadcastSequenceNumber = 0;
constexpr int kMaximumNetlinkMessageWaitMilliSeconds = 300;
uint8_t ReceiveBuffer[kReceiveBufferSize];

void AppendPacket(vector<unique_ptr<const NL80211Packet>>* vec,
                  unique_ptr<const NL80211Packet> packet) {
  vec->push_back(std::move(packet));
}

}

NetlinkManager::NetlinkManager(EventLoop* event_loop)
    : started_(false),
      event_loop_(event_loop),
      sequence_number_(0) {
}

NetlinkManager::~NetlinkManager() {
}

uint32_t NetlinkManager::GetSequenceNumber() {
  if (++sequence_number_ == kBroadcastSequenceNumber) {
    ++sequence_number_;
  }
  return sequence_number_;
}

void NetlinkManager::ReceivePacketAndRunHandler(int fd) {
  ssize_t len = read(fd, ReceiveBuffer, kReceiveBufferSize);
  if (len == -1) {
    LOG(ERROR) << "Failed to read packet from buffer";
    return;
  }
  if (len == 0) {
    return;
  }
  // There might be multiple message in one datagram payload.
  uint8_t* ptr = ReceiveBuffer;
  while (ptr < ReceiveBuffer + len) {
    // peek at the header.
    if (ptr + sizeof(nlmsghdr) > ReceiveBuffer + len) {
      LOG(ERROR) << "payload is broken.";
      return;
    }
    const nlmsghdr* nl_header = reinterpret_cast<const nlmsghdr*>(ptr);
    unique_ptr<NL80211Packet> packet(
        new NL80211Packet(vector<uint8_t>(ptr, ptr + nl_header->nlmsg_len)));
    ptr += nl_header->nlmsg_len;
    if (!packet->IsValid()) {
      LOG(ERROR) << "Receive invalid packet";
      return;
    }
    // Some document says message from kernel should have port id equal 0.
    // However in practice this is not always true so we don't check that.

    uint32_t sequence_number = packet->GetMessageSequence();

    // Handle multicasts.
    if (sequence_number == kBroadcastSequenceNumber) {
      BroadcastHandler(std::move(packet));
      continue;
    }

    auto itr = message_handlers_.find(sequence_number);
    // There is no handler for this sequence number.
    if (itr == message_handlers_.end()) {
      LOG(WARNING) << "No handler for message: " << sequence_number;
      return;
    }
    // A multipart message is terminated by NLMSG_DONE.
    // In this case we don't need to run the handler.
    // NLMSG_NOOP means no operation, message must be discarded.
    uint32_t message_type =  packet->GetMessageType();
    if (message_type == NLMSG_DONE || message_type == NLMSG_NOOP) {
      message_handlers_.erase(itr);
      return;
    }
    if (message_type == NLMSG_OVERRUN) {
      LOG(ERROR) << "Get message overrun notification";
      message_handlers_.erase(itr);
      return;
    }

    // In case we receive a NLMSG_ERROR message:
    // NLMSG_ERROR could be either an error or an ACK.
    // It is an ACK message only when error code field is set to 0.
    // An ACK could be return when we explicitly request that with NLM_F_ACK.
    // An ERROR could be received on NLM_F_ACK or other failure cases.
    // We should still run handler in this case, leaving it for the caller
    // to decide what to do with the packet.

    bool is_multi = packet->IsMulti();
    // Run the handler.
    itr->second(std::move(packet));
    // Remove handler after processing.
    if (!is_multi) {
      message_handlers_.erase(itr);
    }
  }
}

void NetlinkManager::OnNewFamily(unique_ptr<const NL80211Packet> packet) {
  if (packet->GetMessageType() != GENL_ID_CTRL) {
    LOG(ERROR) << "Wrong message type for new family message";
    return;
  }
  if (packet->GetCommand() != CTRL_CMD_NEWFAMILY) {
    LOG(ERROR) << "Wrong command for new family message";
    return;
  }
  uint16_t family_id;
  if (!packet->GetAttributeValue(CTRL_ATTR_FAMILY_ID, &family_id)) {
    LOG(ERROR) << "Failed to get family id";
    return;
  }
  string family_name;
  if (!packet->GetAttributeValue(CTRL_ATTR_FAMILY_NAME, &family_name)) {
    LOG(ERROR) << "Failed to get family name";
    return;
  }
  if (family_name != NL80211_GENL_NAME) {
    LOG(WARNING) << "Ignoring none nl80211 netlink families";
  }
  MessageType nl80211_type(family_id);
  message_types_[family_name] = nl80211_type;
  // Exract multicast groups.
  NL80211NestedAttr multicast_groups(0);
  if (packet->GetAttribute(CTRL_ATTR_MCAST_GROUPS, &multicast_groups)) {
    vector<NL80211NestedAttr> groups;
    if (!multicast_groups.GetListOfNestedAttributes(&groups)) {
      return;
    }
    for (auto& group : groups) {
      string group_name;
      uint32_t group_id;
      if (!group.GetAttributeValue(CTRL_ATTR_MCAST_GRP_NAME, &group_name)) {
        LOG(ERROR) << "Failed to get group name";
      }
      if (!group.GetAttributeValue(CTRL_ATTR_MCAST_GRP_ID, &group_id)) {
        LOG(ERROR) << "Failed to get group id";
      }
      message_types_[family_name].groups[group_name] = group_id;
    }
  }
}

bool NetlinkManager::Start() {
  if (started_) {
    LOG(DEBUG) << "NetlinkManager is already started";
    return true;
  }
  bool setup_rt = SetupSocket(&sync_netlink_fd_);
  if (!setup_rt) {
    LOG(ERROR) << "Failed to setup synchronous netlink socket";
    return false;
  }

  setup_rt = SetupSocket(&async_netlink_fd_);
  if (!setup_rt) {
    LOG(ERROR) << "Failed to setup asynchronous netlink socket";
    return false;
  }

  // Request family id for nl80211 messages.
  if (!DiscoverFamilyId()) {
    return false;
  }
  // Watch socket.
  if (!WatchSocket(&async_netlink_fd_)) {
    return false;
  }
  // Subscribe kernel NL80211 broadcast of regulatory changes.
  if (!SubscribeToEvents(NL80211_MULTICAST_GROUP_REG)) {
    return false;
  }
  // Subscribe kernel NL80211 broadcast of scanning events.
  if (!SubscribeToEvents(NL80211_MULTICAST_GROUP_SCAN)) {
    return false;
  }
  // Subscribe kernel NL80211 broadcast of MLME events.
  if (!SubscribeToEvents(NL80211_MULTICAST_GROUP_MLME)) {
    return false;
  }

  started_ = true;
  return true;
}

bool NetlinkManager::IsStarted() const {
  return started_;
}

bool NetlinkManager::RegisterHandlerAndSendMessage(
    const NL80211Packet& packet,
    std::function<void(unique_ptr<const NL80211Packet>)> handler) {
  if (packet.IsDump()) {
    LOG(ERROR) << "Do not use asynchronous interface for dump request !";
    return false;
  }
  if (!SendMessageInternal(packet, async_netlink_fd_.get())) {
    return false;
  }
  message_handlers_[packet.GetMessageSequence()] = handler;
  return true;
}

bool NetlinkManager::SendMessageAndGetResponses(
    const NL80211Packet& packet,
    vector<unique_ptr<const NL80211Packet>>* response) {
  if (!SendMessageInternal(packet, sync_netlink_fd_.get())) {
    return false;
  }
  // Polling netlink socket, waiting for GetFamily reply.
  struct pollfd netlink_output;
  memset(&netlink_output, 0, sizeof(netlink_output));
  netlink_output.fd = sync_netlink_fd_.get();
  netlink_output.events = POLLIN;

  uint32_t sequence = packet.GetMessageSequence();

  int time_remaining = kMaximumNetlinkMessageWaitMilliSeconds;
  // Multipart messages may come with seperated datagrams, ending with a
  // NLMSG_DONE message.
  // ReceivePacketAndRunHandler() will remove the handler after receiving a
  // NLMSG_DONE message.
  message_handlers_[sequence] = std::bind(AppendPacket, response, _1);

  while (time_remaining > 0 &&
      message_handlers_.find(sequence) != message_handlers_.end()) {
    nsecs_t interval = systemTime(SYSTEM_TIME_MONOTONIC);
    int poll_return = poll(&netlink_output,
                           1,
                           time_remaining);

    if (poll_return == 0) {
      LOG(ERROR) << "Failed to poll netlink fd: time out ";
      message_handlers_.erase(sequence);
      return false;
    } else if (poll_return == -1) {
      LOG(ERROR) << "Failed to poll netlink fd: " << strerror(errno);
      message_handlers_.erase(sequence);
      return false;
    }
    ReceivePacketAndRunHandler(sync_netlink_fd_.get());
    interval = systemTime(SYSTEM_TIME_MONOTONIC) - interval;
    time_remaining -= static_cast<int>(ns2ms(interval));
  }
  if (time_remaining <= 0) {
    LOG(ERROR) << "Timeout waiting for netlink reply messages";
    message_handlers_.erase(sequence);
    return false;
  }
  return true;
}

bool NetlinkManager::SendMessageAndGetSingleResponse(
    const NL80211Packet& packet,
    unique_ptr<const NL80211Packet>* response) {
  unique_ptr<const NL80211Packet> response_or_error;
  if (!SendMessageAndGetSingleResponseOrError(packet, &response_or_error)) {
    return false;
  }
  if (response_or_error->GetMessageType() == NLMSG_ERROR) {
    // We use ERROR because we are not expecting to receive a ACK here.
    // In that case the caller should use |SendMessageAndGetAckOrError|.
    LOG(ERROR) << "Received error message: "
               << strerror(response_or_error->GetErrorCode());
    return false;
  }
  *response = std::move(response_or_error);
  return true;
}

bool NetlinkManager::SendMessageAndGetSingleResponseOrError(
    const NL80211Packet& packet,
    unique_ptr<const NL80211Packet>* response) {
  vector<unique_ptr<const NL80211Packet>> response_vec;
  if (!SendMessageAndGetResponses(packet, &response_vec)) {
    return false;
  }
  if (response_vec.size() != 1) {
    LOG(ERROR) << "Unexpected response size: " << response_vec.size();
    return false;
  }

  *response = std::move(response_vec[0]);
  return true;
}

bool NetlinkManager::SendMessageAndGetAckOrError(const NL80211Packet& packet,
                                                 int* error_code) {
  unique_ptr<const NL80211Packet> response;
  if (!SendMessageAndGetSingleResponseOrError(packet, &response)) {
    return false;
  }
  uint16_t type = response->GetMessageType();
  if (type != NLMSG_ERROR) {
    LOG(ERROR) << "Receive unexpected message type :" << type;
    return false;
  }

  *error_code = response->GetErrorCode();
  return true;
}

bool NetlinkManager::SendMessageAndGetAck(const NL80211Packet& packet) {
  int error_code;
  if (!SendMessageAndGetAckOrError(packet, &error_code)) {
    return false;
  }
  if (error_code != 0) {
    LOG(ERROR) << "Received error messsage: " << strerror(error_code);
    return false;
  }

  return true;
}

bool NetlinkManager::SendMessageInternal(const NL80211Packet& packet, int fd) {
  const vector<uint8_t>& data = packet.GetConstData();
  ssize_t bytes_sent =
      TEMP_FAILURE_RETRY(send(fd, data.data(), data.size(), 0));
  if (bytes_sent == -1) {
    LOG(ERROR) << "Failed to send netlink message: " << strerror(errno);
    return false;
  }
  return true;
}

bool NetlinkManager::SetupSocket(unique_fd* netlink_fd) {
  struct sockaddr_nl nladdr;

  memset(&nladdr, 0, sizeof(nladdr));
  nladdr.nl_family = AF_NETLINK;

  netlink_fd->reset(
      socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_GENERIC));
  if (netlink_fd->get() < 0) {
    LOG(ERROR) << "Failed to create netlink socket: " << strerror(errno);
    return false;
  }
  // Set maximum receive buffer size.
  // Datagram which is larger than this size will be discarded.
  if (setsockopt(netlink_fd->get(),
                 SOL_SOCKET,
                 SO_RCVBUFFORCE,
                 &kReceiveBufferSize,
                 sizeof(kReceiveBufferSize)) < 0) {
    LOG(ERROR) << "Failed to set uevent socket SO_RCVBUFFORCE option: " << strerror(errno);
    return false;
  }
  if (bind(netlink_fd->get(),
           reinterpret_cast<struct sockaddr*>(&nladdr),
           sizeof(nladdr)) < 0) {
    LOG(ERROR) << "Failed to bind netlink socket: " << strerror(errno);
    return false;
  }
  return true;
}

bool NetlinkManager::WatchSocket(unique_fd* netlink_fd) {
  // Watch socket
  bool watch_fd_rt = event_loop_->WatchFileDescriptor(
      netlink_fd->get(),
      EventLoop::kModeInput,
      std::bind(&NetlinkManager::ReceivePacketAndRunHandler, this, _1));
  if (!watch_fd_rt) {
    LOG(ERROR) << "Failed to watch fd: " << netlink_fd->get();
    return false;
  }
  return true;
}

uint16_t NetlinkManager::GetFamilyId() {
  return message_types_[NL80211_GENL_NAME].family_id;
}

bool NetlinkManager::DiscoverFamilyId() {
  NL80211Packet get_family_request(GENL_ID_CTRL,
                                   CTRL_CMD_GETFAMILY,
                                   GetSequenceNumber(),
                                   getpid());
  NL80211Attr<string> family_name(CTRL_ATTR_FAMILY_NAME, NL80211_GENL_NAME);
  get_family_request.AddAttribute(family_name);
  unique_ptr<const NL80211Packet> response;
  if (!SendMessageAndGetSingleResponse(get_family_request, &response)) {
    LOG(ERROR) << "Failed to get NL80211 family info";
    return false;
  }
  OnNewFamily(std::move(response));
  if (message_types_.find(NL80211_GENL_NAME) == message_types_.end()) {
    LOG(ERROR) << "Failed to get NL80211 family id";
    return false;
  }
  return true;
}

bool NetlinkManager::SubscribeToEvents(const string& group) {
  auto groups = message_types_[NL80211_GENL_NAME].groups;
  if (groups.find(group) == groups.end()) {
    LOG(ERROR) << "Failed to subscribe: group " << group << " doesn't exist";
    return false;
  }
  uint32_t group_id = groups[group];
  int err = setsockopt(async_netlink_fd_.get(),
                       SOL_NETLINK,
                       NETLINK_ADD_MEMBERSHIP,
                       &group_id,
                       sizeof(group_id));
  if (err < 0) {
    LOG(ERROR) << "Failed to setsockopt: " << strerror(errno);
    return false;
  }
  return true;
}

void NetlinkManager::BroadcastHandler(unique_ptr<const NL80211Packet> packet) {
  if (packet->GetMessageType() != GetFamilyId()) {
    LOG(ERROR) << "Wrong family id for multicast message";
    return;
  }
  uint32_t command = packet->GetCommand();

  if (command == NL80211_CMD_NEW_SCAN_RESULTS ||
      // Scan was aborted, for unspecified reasons.partial scan results may be
      // available.
      command == NL80211_CMD_SCAN_ABORTED) {
    OnScanResultsReady(std::move(packet));
    return;
  }

  if (command == NL80211_CMD_SCHED_SCAN_RESULTS ||
      command == NL80211_CMD_SCHED_SCAN_STOPPED) {
    OnSchedScanResultsReady(std::move(packet));
    return;
  }


  // Driver which supports SME uses both NL80211_CMD_AUTHENTICATE and
  // NL80211_CMD_ASSOCIATE, otherwise it uses NL80211_CMD_CONNECT
  // to notify a combination of authentication and association processses.
  // Currently we monitor CONNECT/ASSOCIATE/ROAM event for up-to-date
  // frequency and bssid.
  // TODO(nywang): Handle other MLME events, which help us track the
  // connection state better.
  if (command == NL80211_CMD_CONNECT ||
      command == NL80211_CMD_ASSOCIATE ||
      command == NL80211_CMD_ROAM ||
      command == NL80211_CMD_DISCONNECT ||
      command == NL80211_CMD_DISASSOCIATE) {
      OnMlmeEvent(std::move(packet));
     return;
  }
  if (command == NL80211_CMD_REG_CHANGE) {
    OnRegChangeEvent(std::move(packet));
    return;
  }
  // Station eventsFor AP mode.
  if (command == NL80211_CMD_NEW_STATION ||
      command == NL80211_CMD_DEL_STATION) {
    uint32_t if_index;
    if (!packet->GetAttributeValue(NL80211_ATTR_IFINDEX, &if_index)) {
      LOG(WARNING) << "Failed to get interface index from station event";
      return;
    }
    const auto handler = on_station_event_handler_.find(if_index);
    if (handler != on_station_event_handler_.end()) {
      vector<uint8_t> mac_address;
      if (!packet->GetAttributeValue(NL80211_ATTR_MAC, &mac_address)) {
        LOG(WARNING) << "Failed to get mac address from station event";
        return;
      }
      if (command == NL80211_CMD_NEW_STATION) {
        handler->second(NEW_STATION, mac_address);
      } else {
        handler->second(DEL_STATION, mac_address);
      }
    }
    return;
  }
}

void NetlinkManager::OnRegChangeEvent(unique_ptr<const NL80211Packet> packet) {
  uint32_t wiphy_index;
  if (!packet->GetAttributeValue(NL80211_ATTR_WIPHY, &wiphy_index)) {
    LOG(ERROR) << "Failed to get wiphy index from reg changed message";
    return;
  }

  uint8_t reg_type;
  if (!packet->GetAttributeValue(NL80211_ATTR_REG_TYPE, &reg_type)) {
    LOG(ERROR) << "Failed to get NL80211_ATTR_REG_TYPE";
  }

  string country_code;
  // NL80211_REGDOM_TYPE_COUNTRY means the regulatory domain set is one that
  // pertains to a specific country
  if (reg_type == NL80211_REGDOM_TYPE_COUNTRY) {
    if (!packet->GetAttributeValue(NL80211_ATTR_REG_ALPHA2, &country_code)) {
      LOG(ERROR) << "Failed to get NL80211_ATTR_REG_ALPHA2";
      return;
    }
  } else if (reg_type == NL80211_REGDOM_TYPE_WORLD ||
      reg_type == NL80211_REGDOM_TYPE_CUSTOM_WORLD ||
      reg_type == NL80211_REGDOM_TYPE_INTERSECTION) {
    // NL80211_REGDOM_TYPE_WORLD refers to the world regulartory domain.
    // NL80211_REGDOM_TYPE_CUSTOM_WORLD refers to the driver specific world
    // regulartory domain.
    // NL80211_REGDOM_TYPE_INTERSECTION refers to an intersection between two
    // regulatory domains:
    // The previously set regulatory domain on the system and the last accepted
    // regulatory domain request to be processed.
    country_code = "";
  } else {
    LOG(ERROR) << "Unknown type of regulatory domain change: " << (int)reg_type;
    return;
  }

  const auto handler = on_reg_domain_changed_handler_.find(wiphy_index);
  if (handler == on_reg_domain_changed_handler_.end()) {
    LOG(DEBUG) << "No handler for country code changed event from wiphy"
               << "with index: " << wiphy_index;
    return;
  }
  handler->second(country_code);
}

void NetlinkManager::OnMlmeEvent(unique_ptr<const NL80211Packet> packet) {
  uint32_t if_index;

  if (!packet->GetAttributeValue(NL80211_ATTR_IFINDEX, &if_index)) {
    LOG(ERROR) << "Failed to get interface index from a MLME event message";
    return;
  }
  const auto handler = on_mlme_event_handler_.find(if_index);
  if (handler == on_mlme_event_handler_.end()) {
    LOG(DEBUG) << "No handler for mlme event from interface"
               << " with index: " << if_index;
    return;
  }
  uint32_t command = packet->GetCommand();
  if (command == NL80211_CMD_CONNECT) {
    auto event = MlmeConnectEvent::InitFromPacket(packet.get());
    if (event != nullptr) {
      handler->second->OnConnect(std::move(event));
    }
    return;
  }
  if (command == NL80211_CMD_ASSOCIATE) {
    auto event = MlmeAssociateEvent::InitFromPacket(packet.get());
    if (event != nullptr) {
      handler->second->OnAssociate(std::move(event));
    }
    return;
  }
  if (command == NL80211_CMD_ROAM) {
    auto event = MlmeRoamEvent::InitFromPacket(packet.get());
    if (event != nullptr) {
      handler->second->OnRoam(std::move(event));
    }
    return;
  }
  if (command == NL80211_CMD_DISCONNECT) {
    auto event = MlmeDisconnectEvent::InitFromPacket(packet.get());
    if (event != nullptr) {
      handler->second->OnDisconnect(std::move(event));
    }
    return;
  }
  if (command == NL80211_CMD_DISASSOCIATE) {
    auto event = MlmeDisassociateEvent::InitFromPacket(packet.get());
    if (event != nullptr) {
      handler->second->OnDisassociate(std::move(event));
    }
    return;
  }

}

void NetlinkManager::OnSchedScanResultsReady(unique_ptr<const NL80211Packet> packet) {
  uint32_t if_index;
  if (!packet->GetAttributeValue(NL80211_ATTR_IFINDEX, &if_index)) {
    LOG(ERROR) << "Failed to get interface index from scan result notification";
    return;
  }

  const auto handler = on_sched_scan_result_ready_handler_.find(if_index);
  if (handler == on_sched_scan_result_ready_handler_.end()) {
    LOG(DEBUG) << "No handler for scheduled scan result notification from"
               << " interface with index: " << if_index;
    return;
  }
  // Run scan result notification handler.
  handler->second(if_index, packet->GetCommand() == NL80211_CMD_SCHED_SCAN_STOPPED);
}

void NetlinkManager::OnScanResultsReady(unique_ptr<const NL80211Packet> packet) {
  uint32_t if_index;
  if (!packet->GetAttributeValue(NL80211_ATTR_IFINDEX, &if_index)) {
    LOG(ERROR) << "Failed to get interface index from scan result notification";
    return;
  }
  bool aborted = false;
  if (packet->GetCommand() == NL80211_CMD_SCAN_ABORTED) {
    aborted = true;
  }

  const auto handler = on_scan_result_ready_handler_.find(if_index);
  if (handler == on_scan_result_ready_handler_.end()) {
    LOG(WARNING) << "No handler for scan result notification from interface"
                 << " with index: " << if_index;
    return;
  }

  vector<vector<uint8_t>> ssids;
  NL80211NestedAttr ssids_attr(0);
  if (!packet->GetAttribute(NL80211_ATTR_SCAN_SSIDS, &ssids_attr)) {
    if (!aborted) {
      LOG(WARNING) << "Failed to get scan ssids from scan result notification";
    }
  } else {
    if (!ssids_attr.GetListOfAttributeValues(&ssids)) {
      return;
    }
  }
  vector<uint32_t> freqs;
  NL80211NestedAttr freqs_attr(0);
  if (!packet->GetAttribute(NL80211_ATTR_SCAN_FREQUENCIES, &freqs_attr)) {
    if (!aborted) {
      LOG(WARNING) << "Failed to get scan freqs from scan result notification";
    }
  } else {
    if (!freqs_attr.GetListOfAttributeValues(&freqs)) {
      return;
    }
  }
  // Run scan result notification handler.
  handler->second(if_index, aborted, ssids, freqs);
}

void NetlinkManager::SubscribeStationEvent(
    uint32_t interface_index,
    OnStationEventHandler handler) {
  on_station_event_handler_[interface_index] = handler;
}

void NetlinkManager::UnsubscribeStationEvent(uint32_t interface_index) {
  on_station_event_handler_.erase(interface_index);
}

void NetlinkManager::SubscribeRegDomainChange(
    uint32_t wiphy_index,
    OnRegDomainChangedHandler handler) {
  on_reg_domain_changed_handler_[wiphy_index] = handler;
}

void NetlinkManager::UnsubscribeRegDomainChange(uint32_t wiphy_index) {
  on_reg_domain_changed_handler_.erase(wiphy_index);
}

void NetlinkManager::SubscribeScanResultNotification(
    uint32_t interface_index,
    OnScanResultsReadyHandler handler) {
  on_scan_result_ready_handler_[interface_index] = handler;
}

void NetlinkManager::UnsubscribeScanResultNotification(
    uint32_t interface_index) {
  on_scan_result_ready_handler_.erase(interface_index);
}

void NetlinkManager::SubscribeMlmeEvent(uint32_t interface_index,
                                        MlmeEventHandler* handler) {
  on_mlme_event_handler_[interface_index] = handler;
}

void NetlinkManager::UnsubscribeMlmeEvent(uint32_t interface_index) {
  on_mlme_event_handler_.erase(interface_index);
}

void NetlinkManager::SubscribeSchedScanResultNotification(
      uint32_t interface_index,
      OnSchedScanResultsReadyHandler handler) {
  on_sched_scan_result_ready_handler_[interface_index] = handler;
}

void NetlinkManager::UnsubscribeSchedScanResultNotification(
    uint32_t interface_index) {
  on_sched_scan_result_ready_handler_.erase(interface_index);
}

}  // namespace wificond
}  // namespace android