普通文本  |  396行  |  14.37 KB

//
// Copyright (C) 2014 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 "apmanager/device_info.h"

#include <linux/netlink.h>
#include <linux/rtnetlink.h>

#include <map>
#include <string>
#include <vector>

#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <shill/net/byte_string.h>
#include <shill/net/mock_netlink_manager.h>
#include "shill/net/netlink_message_matchers.h"
#include "shill/net/nl80211_attribute.h"
#include "shill/net/nl80211_message.h"
#include <shill/net/rtnl_message.h>

#include "apmanager/fake_device_adaptor.h"
#include "apmanager/mock_control.h"
#include "apmanager/mock_device.h"
#include "apmanager/mock_manager.h"

using shill::ByteString;
using shill::Nl80211Message;
using shill::RTNLMessage;
using std::map;
using std::string;
using std::vector;
using ::testing::_;
using ::testing::Mock;
using ::testing::ReturnNew;

namespace apmanager {

namespace {

const char kTestDeviceName[] = "test-phy";
const char kTestInterface0Name[] = "test-interface0";
const char kTestInterface1Name[] = "test-interface1";
const uint32_t kTestInterface0Index = 1000;
const uint32_t kTestInterface1Index = 1001;

}  // namespace

class DeviceInfoTest : public testing::Test {
 public:
  DeviceInfoTest()
      : manager_(&control_interface_),
        device_info_(&manager_) {}
  virtual ~DeviceInfoTest() {}

  virtual void SetUp() {
    // Setup temporary directory for device info files.
    CHECK(temp_dir_.CreateUniqueTempDir());
    device_info_root_ = temp_dir_.path().Append("sys/class/net");
    device_info_.device_info_root_ = device_info_root_;

    // Setup mock pointers;
    device_info_.netlink_manager_ = &netlink_manager_;

    ON_CALL(control_interface_, CreateDeviceAdaptorRaw())
      .WillByDefault(ReturnNew<FakeDeviceAdaptor>());
  }

  bool IsWifiInterface(const string& interface_name) {
    return device_info_.IsWifiInterface(interface_name);
  }

  void CreateDeviceInfoFile(const string& interface_name,
                            const string& file_name,
                            const string& contents) {
    base::FilePath info_path =
        device_info_root_.Append(interface_name).Append(file_name);
    EXPECT_TRUE(base::CreateDirectory(info_path.DirName()));
    EXPECT_TRUE(base::WriteFile(info_path, contents.c_str(), contents.size()));
  }

  void SendLinkMsg(RTNLMessage::Mode mode,
                   uint32_t interface_index,
                   const string& interface_name) {
    RTNLMessage message(RTNLMessage::kTypeLink,
                        mode,
                        0,
                        0,
                        0,
                        interface_index,
                        shill::IPAddress::kFamilyIPv4);
    message.SetAttribute(static_cast<uint16_t>(IFLA_IFNAME),
                         ByteString(interface_name, true));
    device_info_.LinkMsgHandler(message);
  }

  void VerifyInterfaceList(const vector<Device::WiFiInterface>& interfaces) {
    // Verify number of elements in the interface infos map and interface index
    // of the elements in the map.
    EXPECT_EQ(interfaces.size(), device_info_.interface_infos_.size());
    for (const auto& interface : interfaces) {
      map<uint32_t, Device::WiFiInterface>::iterator it =
          device_info_.interface_infos_.find(interface.iface_index);
      EXPECT_NE(device_info_.interface_infos_.end(), it);
      EXPECT_TRUE(interface.Equals(it->second));
    }
  }

  void VerifyDeviceList(const vector<scoped_refptr<Device>>& devices) {
    // Verify number of elements in the device map and the elements in the map.
    EXPECT_EQ(devices.size(), device_info_.devices_.size());
    for (const auto& device : devices) {
      map<string, scoped_refptr<Device>>::iterator it =
          device_info_.devices_.find(device->GetDeviceName());
      EXPECT_NE(device_info_.devices_.end(), it);
      EXPECT_EQ(device, it->second);
    }
  }
  void AddInterface(const Device::WiFiInterface& interface) {
    device_info_.interface_infos_[interface.iface_index] = interface;
  }

  void OnWiFiPhyInfoReceived(const Nl80211Message& message) {
    device_info_.OnWiFiPhyInfoReceived(message);
  }

  void OnWiFiInterfaceInfoReceived(const Nl80211Message& message) {
    device_info_.OnWiFiInterfaceInfoReceived(message);
  }

  void OnWiFiInterfacePhyInfoReceived(uint32_t interface_index,
                                      const Nl80211Message& message) {
    device_info_.OnWiFiInterfacePhyInfoReceived(interface_index, message);
  }

  void RegisterDevice(scoped_refptr<Device> device) {
    device_info_.RegisterDevice(device);
  }

 protected:
  MockControl control_interface_;
  MockManager manager_;

  shill::MockNetlinkManager netlink_manager_;
  base::ScopedTempDir temp_dir_;
  base::FilePath device_info_root_;
  DeviceInfo device_info_;
};

MATCHER_P2(IsGetInfoMessage, command, index, "") {
  if (arg->message_type() != Nl80211Message::GetMessageType()) {
    return false;
  }
  const Nl80211Message *msg = reinterpret_cast<const Nl80211Message *>(arg);
  if (msg->command() != command) {
    return false;
  }
  uint32_t interface_index;
  if (!msg->const_attributes()->GetU32AttributeValue(NL80211_ATTR_IFINDEX,
                                                     &interface_index)) {
    return false;
  }
  // kInterfaceIndex is signed, but the attribute as handed from the kernel
  // is unsigned.  We're silently casting it away with this assignment.
  uint32_t test_interface_index = index;
  return interface_index == test_interface_index;
}

MATCHER_P(IsInterface, interface, "") {
  return arg.Equals(interface);
}

MATCHER_P(IsDevice, device_name, "") {
  return arg->GetDeviceName() == device_name;
}

TEST_F(DeviceInfoTest, EnumerateDevices) {
  shill::NewWiphyMessage message;

  // No device name in the message, failed to create device.
  EXPECT_CALL(manager_, RegisterDevice(_)).Times(0);
  OnWiFiPhyInfoReceived(message);

  // Device name in the message, device should be created/register to manager.
  message.attributes()->CreateNl80211Attribute(
      NL80211_ATTR_WIPHY_NAME, shill::NetlinkMessage::MessageContext());
  message.attributes()->SetStringAttributeValue(NL80211_ATTR_WIPHY_NAME,
                                                kTestDeviceName);
  EXPECT_CALL(manager_, RegisterDevice(IsDevice(kTestDeviceName))).Times(1);
  OnWiFiPhyInfoReceived(message);
  Mock::VerifyAndClearExpectations(&manager_);

  // Receive a message for a device already created, should not create/register
  // device again.
  EXPECT_CALL(manager_, RegisterDevice(_)).Times(0);
  OnWiFiPhyInfoReceived(message);
}

TEST_F(DeviceInfoTest, IsWiFiInterface) {
  // No device info file exist, not a wifi interface.
  EXPECT_FALSE(IsWifiInterface(kTestInterface0Name));

  // Device info for an ethernet device, not a wifi interface
  CreateDeviceInfoFile(kTestInterface0Name, "uevent", "INTERFACE=eth0\n");
  EXPECT_FALSE(IsWifiInterface(kTestInterface0Name));

  // Device info for a wifi interface.
  CreateDeviceInfoFile(kTestInterface1Name, "uevent", "DEVTYPE=wlan\n");
  EXPECT_TRUE(IsWifiInterface(kTestInterface1Name));
}

TEST_F(DeviceInfoTest, InterfaceDetection) {
  vector<Device::WiFiInterface> interface_list;
  // Ignore non-wifi interface.
  SendLinkMsg(RTNLMessage::kModeAdd,
              kTestInterface0Index,
              kTestInterface0Name);
  VerifyInterfaceList(interface_list);

  // AddLink event for wifi interface.
  CreateDeviceInfoFile(kTestInterface0Name, "uevent", "DEVTYPE=wlan\n");
  EXPECT_CALL(netlink_manager_, SendNl80211Message(
      IsGetInfoMessage(NL80211_CMD_GET_INTERFACE, kTestInterface0Index),
      _, _, _)).Times(1);
  SendLinkMsg(RTNLMessage::kModeAdd,
              kTestInterface0Index,
              kTestInterface0Name);
  interface_list.push_back(Device::WiFiInterface(
      kTestInterface0Name, "", kTestInterface0Index, 0));
  VerifyInterfaceList(interface_list);
  Mock::VerifyAndClearExpectations(&netlink_manager_);

  // AddLink event for another wifi interface.
  CreateDeviceInfoFile(kTestInterface1Name, "uevent", "DEVTYPE=wlan\n");
  EXPECT_CALL(netlink_manager_, SendNl80211Message(
      IsGetInfoMessage(NL80211_CMD_GET_INTERFACE, kTestInterface1Index),
      _, _, _)).Times(1);
  SendLinkMsg(RTNLMessage::kModeAdd,
              kTestInterface1Index,
              kTestInterface1Name);
  interface_list.push_back(Device::WiFiInterface(
      kTestInterface1Name, "", kTestInterface1Index, 0));
  VerifyInterfaceList(interface_list);
  Mock::VerifyAndClearExpectations(&netlink_manager_);

  // AddLink event for an interface that's already added, no change to interface
  // list.
  EXPECT_CALL(netlink_manager_, SendNl80211Message(_, _, _, _)).Times(0);
  SendLinkMsg(RTNLMessage::kModeAdd,
              kTestInterface0Index,
              kTestInterface0Name);
  VerifyInterfaceList(interface_list);
  Mock::VerifyAndClearExpectations(&netlink_manager_);

  // Remove the first wifi interface.
  SendLinkMsg(RTNLMessage::kModeDelete,
              kTestInterface0Index,
              kTestInterface0Name);
  interface_list.clear();
  interface_list.push_back(Device::WiFiInterface(
      kTestInterface1Name, "", kTestInterface1Index, 0));
  VerifyInterfaceList(interface_list);

  // Remove the non-exist interface, no change to the list.
  SendLinkMsg(RTNLMessage::kModeDelete,
              kTestInterface0Index,
              kTestInterface0Name);
  VerifyInterfaceList(interface_list);

  // Remove the last interface, list should be empty now.
  SendLinkMsg(RTNLMessage::kModeDelete,
              kTestInterface1Index,
              kTestInterface1Name);
  interface_list.clear();
  VerifyInterfaceList(interface_list);
}

TEST_F(DeviceInfoTest, ParseWifiInterfaceInfo) {
  // Add an interface without interface type info.
  Device::WiFiInterface interface(
      kTestInterface0Name, "", kTestInterface0Index, 0);
  AddInterface(interface);
  vector<Device::WiFiInterface> interface_list;
  interface_list.push_back(interface);

  // Message contain no interface index, no change to the interface info.
  shill::NewInterfaceMessage message;
  OnWiFiInterfaceInfoReceived(message);
  VerifyInterfaceList(interface_list);

  // Message contain no interface type, no change to the interface info.
  message.attributes()->CreateNl80211Attribute(
      NL80211_ATTR_IFINDEX, shill::NetlinkMessage::MessageContext());
  message.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX,
                                             kTestInterface0Index);
  OnWiFiInterfaceInfoReceived(message);

  // Message contain interface type, interface info should be updated with
  // the interface type, and a new Nl80211 message should be send to query for
  // the PHY info.
  EXPECT_CALL(netlink_manager_, SendNl80211Message(
      IsGetInfoMessage(NL80211_CMD_GET_WIPHY, kTestInterface0Index),
      _, _, _)).Times(1);
  message.attributes()->CreateNl80211Attribute(
      NL80211_ATTR_IFTYPE, shill::NetlinkMessage::MessageContext());
  message.attributes()->SetU32AttributeValue(NL80211_ATTR_IFTYPE,
                                             NL80211_IFTYPE_AP);
  OnWiFiInterfaceInfoReceived(message);
  interface_list[0].iface_type = NL80211_IFTYPE_AP;
  VerifyInterfaceList(interface_list);
}

TEST_F(DeviceInfoTest, ParsePhyInfoForWifiInterface) {
  // Register a mock device.
  scoped_refptr<MockDevice> device = new MockDevice(&manager_);
  device->SetDeviceName(kTestDeviceName);
  EXPECT_CALL(manager_, RegisterDevice(_)).Times(1);
  RegisterDevice(device);

  // PHY info message.
  shill::NewWiphyMessage message;
  message.attributes()->CreateNl80211Attribute(
      NL80211_ATTR_WIPHY_NAME, shill::NetlinkMessage::MessageContext());
  message.attributes()->SetStringAttributeValue(NL80211_ATTR_WIPHY_NAME,
                                                kTestDeviceName);

  // Receive PHY info message for an interface that have not been detected yet.
  EXPECT_CALL(*device.get(), RegisterInterface(_)).Times(0);
  OnWiFiInterfacePhyInfoReceived(kTestInterface0Index, message);

  // Pretend interface is detected through AddLink with interface info already
  // received (interface type), and still missing PHY info for that interface.
  Device::WiFiInterface interface(
      kTestInterface0Name, "", kTestInterface0Index, NL80211_IFTYPE_AP);
  AddInterface(interface);

  // PHY info is received for a detected interface, should register that
  // interface to the corresponding Device.
  interface.device_name = kTestDeviceName;
  EXPECT_CALL(*device.get(),
              RegisterInterface(IsInterface(interface))).Times(1);
  OnWiFiInterfacePhyInfoReceived(kTestInterface0Index, message);
}

TEST_F(DeviceInfoTest, ReceivePhyInfoBeforePhyIsEnumerated) {
  // New interface is detected.
  Device::WiFiInterface interface(
      kTestInterface0Name, "", kTestInterface0Index, NL80211_IFTYPE_AP);
  AddInterface(interface);
  vector<Device::WiFiInterface> interface_list;
  interface_list.push_back(interface);

  // Received PHY info for the interface when the corresponding PHY is not
  // enumerated yet, new device should be created and register to manager.
  shill::NewWiphyMessage message;
  message.attributes()->CreateNl80211Attribute(
      NL80211_ATTR_WIPHY_NAME, shill::NetlinkMessage::MessageContext());
  message.attributes()->SetStringAttributeValue(NL80211_ATTR_WIPHY_NAME,
                                                kTestDeviceName);
  EXPECT_CALL(manager_, RegisterDevice(IsDevice(kTestDeviceName))).Times(1);
  OnWiFiInterfacePhyInfoReceived(kTestInterface0Index, message);
  interface_list[0].device_name = kTestDeviceName;
  VerifyInterfaceList(interface_list);
}

TEST_F(DeviceInfoTest, RegisterDevice) {
  vector<scoped_refptr<Device>> device_list;

  // Register a nullptr.
  RegisterDevice(nullptr);
  VerifyDeviceList(device_list);

  // Register a device.
  device_list.push_back(new Device(&manager_, kTestDeviceName, 0));
  EXPECT_CALL(manager_, RegisterDevice(device_list[0]));
  RegisterDevice(device_list[0]);
  VerifyDeviceList(device_list);
}

}  // namespace apmanager