| /* |
| * 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 <functional> |
| #include <memory> |
| #include <vector> |
| |
| #include <linux/netlink.h> |
| |
| #include <gtest/gtest.h> |
| |
| #include "android/net/wifi/IWifiScannerImpl.h" |
| #include "wificond/net/kernel-header-latest/nl80211.h" |
| #include "wificond/scanning/scan_result.h" |
| #include "wificond/scanning/scan_utils.h" |
| #include "wificond/tests/mock_netlink_manager.h" |
| |
| using std::bind; |
| using std::placeholders::_1; |
| using std::placeholders::_2; |
| using std::unique_ptr; |
| using std::vector; |
| using testing::AllOf; |
| using testing::Invoke; |
| using testing::NiceMock; |
| using testing::Not; |
| using testing::Return; |
| using testing::_; |
| |
| using android::net::wifi::IWifiScannerImpl; |
| using com::android::server::wifi::wificond::NativeScanResult; |
| |
| namespace android { |
| namespace wificond { |
| |
| namespace { |
| |
| constexpr uint32_t kFakeInterfaceIndex = 12; |
| constexpr uint32_t kFakeScheduledScanIntervalMs = 20000; |
| constexpr uint32_t kFakeSequenceNumber = 1984; |
| constexpr int kFakeErrorCode = EIO; |
| constexpr int32_t kFake2gRssiThreshold = -80; |
| constexpr int32_t kFake5gRssiThreshold = -77; |
| constexpr bool kFakeUseRandomMAC = true; |
| constexpr bool kFakeRequestLowPower = true; |
| constexpr int kFakeScanType = IWifiScannerImpl::SCAN_TYPE_LOW_SPAN; |
| |
| // Currently, control messages are only created by the kernel and sent to us. |
| // Therefore NL80211Packet doesn't have corresponding constructor. |
| // For test we manually create control messages using this helper function. |
| NL80211Packet CreateControlMessageError(int error_code) { |
| vector<uint8_t> data; |
| data.resize(NLMSG_HDRLEN + NLA_ALIGN(sizeof(int)), 0); |
| // Initialize length field. |
| nlmsghdr* nl_header = reinterpret_cast<nlmsghdr*>(data.data()); |
| nl_header->nlmsg_len = data.size(); |
| nl_header->nlmsg_type = NLMSG_ERROR; |
| nl_header->nlmsg_seq = kFakeSequenceNumber; |
| nl_header->nlmsg_pid = getpid(); |
| int* error_field = reinterpret_cast<int*>(data.data() + NLMSG_HDRLEN); |
| *error_field = -error_code; |
| |
| return NL80211Packet(data); |
| } |
| |
| NL80211Packet CreateControlMessageAck() { |
| return CreateControlMessageError(0); |
| } |
| |
| // This is a helper function to mock the behavior of NetlinkManager:: |
| // SendMessageAndGetResponses() when we expect a single packet response. |
| // |request_message| and |response| are mapped to existing parameters of |
| // SendMessageAndGetResponses(). |
| // |mock_response| and |mock_return value| are additional parameters used |
| // for specifying expected results, |
| bool AppendMessageAndReturn( |
| NL80211Packet& mock_response, |
| bool mock_return_value, |
| const NL80211Packet& request_message, |
| vector<std::unique_ptr<const NL80211Packet>>* response) { |
| response->push_back(std::make_unique<NL80211Packet>(mock_response)); |
| return mock_return_value; |
| } |
| |
| } // namespace |
| |
| class ScanUtilsTest : public ::testing::Test { |
| protected: |
| virtual void SetUp() { |
| ON_CALL(netlink_manager_, |
| SendMessageAndGetResponses(_, _)).WillByDefault(Return(true)); |
| } |
| |
| NiceMock<MockNetlinkManager> netlink_manager_; |
| ScanUtils scan_utils_{&netlink_manager_}; |
| }; |
| |
| MATCHER_P(DoesNL80211PacketMatchCommand, command, |
| "Check if the netlink packet matches |command|") { |
| return arg.GetCommand() == command; |
| } |
| |
| MATCHER_P(DoesNL80211PacketHaveAttribute, attr, |
| "Check if the netlink packet has atttribute |attr|") { |
| return arg.HasAttribute(attr); |
| } |
| |
| MATCHER_P2(DoesNL80211PacketHaveAttributeWithUint32Value, attr, expected_value, |
| "Check if the netlink packet has atttribute |attr| with " |
| "|expected_value|") { |
| uint32_t actual_value; |
| if (!arg.GetAttributeValue(attr, &actual_value)) { |
| return false; |
| } |
| return actual_value == expected_value; |
| } |
| |
| TEST_F(ScanUtilsTest, CanGetScanResult) { |
| vector<NativeScanResult> scan_results; |
| EXPECT_CALL( |
| netlink_manager_, |
| SendMessageAndGetResponses( |
| DoesNL80211PacketMatchCommand(NL80211_CMD_GET_SCAN), _)); |
| |
| // We don't use EXPECT_TRUE here because we need to mock a complete |
| // response for NL80211_CMD_GET_SCAN to satisfy the parsing code called |
| // by GetScanResult. |
| // TODO(b/34231002): Mock response for NL80211_CMD_GET_SCAN. |
| // TODO(b/34231420): Add validation of interface index. |
| scan_utils_.GetScanResult(kFakeInterfaceIndex, &scan_results); |
| } |
| |
| TEST_F(ScanUtilsTest, CanSendScanRequest) { |
| NL80211Packet response = CreateControlMessageAck(); |
| EXPECT_CALL( |
| netlink_manager_, |
| SendMessageAndGetResponses( |
| DoesNL80211PacketMatchCommand(NL80211_CMD_TRIGGER_SCAN), _)). |
| WillOnce(Invoke(bind( |
| AppendMessageAndReturn, response, true, _1, _2))); |
| |
| int errno_ignored; |
| EXPECT_TRUE(scan_utils_.Scan(kFakeInterfaceIndex, kFakeUseRandomMAC, |
| kFakeScanType, {}, {}, &errno_ignored)); |
| // TODO(b/34231420): Add validation of requested scan ssids, threshold, |
| // and frequencies. |
| } |
| |
| TEST_F(ScanUtilsTest, CanSendScanRequestWithRandomAddr) { |
| NL80211Packet response = CreateControlMessageAck(); |
| EXPECT_CALL( |
| netlink_manager_, |
| SendMessageAndGetResponses( |
| AllOf( |
| DoesNL80211PacketMatchCommand(NL80211_CMD_TRIGGER_SCAN), |
| DoesNL80211PacketHaveAttributeWithUint32Value( |
| NL80211_ATTR_SCAN_FLAGS, NL80211_SCAN_FLAG_RANDOM_ADDR)), |
| _)). |
| WillOnce(Invoke(bind(AppendMessageAndReturn, response, true, _1, _2))); |
| |
| int errno_ignored; |
| EXPECT_TRUE(scan_utils_.Scan(kFakeInterfaceIndex, true, |
| IWifiScannerImpl::SCAN_TYPE_DEFAULT, |
| {}, {}, &errno_ignored)); |
| } |
| |
| TEST_F(ScanUtilsTest, CanSendScanRequestForLowSpanScan) { |
| NL80211Packet response = CreateControlMessageAck(); |
| EXPECT_CALL( |
| netlink_manager_, |
| SendMessageAndGetResponses( |
| AllOf( |
| DoesNL80211PacketMatchCommand(NL80211_CMD_TRIGGER_SCAN), |
| DoesNL80211PacketHaveAttributeWithUint32Value( |
| NL80211_ATTR_SCAN_FLAGS, NL80211_SCAN_FLAG_LOW_SPAN)), |
| _)). |
| WillOnce(Invoke(bind(AppendMessageAndReturn, response, true, _1, _2))); |
| |
| int errno_ignored; |
| EXPECT_TRUE(scan_utils_.Scan(kFakeInterfaceIndex, false, |
| IWifiScannerImpl::SCAN_TYPE_LOW_SPAN, |
| {}, {}, &errno_ignored)); |
| } |
| |
| TEST_F(ScanUtilsTest, CanSendScanRequestForLowPowerScan) { |
| NL80211Packet response = CreateControlMessageAck(); |
| EXPECT_CALL( |
| netlink_manager_, |
| SendMessageAndGetResponses( |
| AllOf( |
| DoesNL80211PacketMatchCommand(NL80211_CMD_TRIGGER_SCAN), |
| DoesNL80211PacketHaveAttributeWithUint32Value( |
| NL80211_ATTR_SCAN_FLAGS, NL80211_SCAN_FLAG_LOW_POWER)), |
| _)). |
| WillOnce(Invoke(bind(AppendMessageAndReturn, response, true, _1, _2))); |
| |
| int errno_ignored; |
| EXPECT_TRUE(scan_utils_.Scan(kFakeInterfaceIndex, false, |
| IWifiScannerImpl::SCAN_TYPE_LOW_POWER, |
| {}, {}, &errno_ignored)); |
| } |
| |
| TEST_F(ScanUtilsTest, CanSendScanRequestForHighAccuracyScan) { |
| NL80211Packet response = CreateControlMessageAck(); |
| EXPECT_CALL( |
| netlink_manager_, |
| SendMessageAndGetResponses( |
| AllOf( |
| DoesNL80211PacketMatchCommand(NL80211_CMD_TRIGGER_SCAN), |
| DoesNL80211PacketHaveAttributeWithUint32Value( |
| NL80211_ATTR_SCAN_FLAGS, NL80211_SCAN_FLAG_HIGH_ACCURACY)), |
| _)). |
| WillOnce(Invoke(bind(AppendMessageAndReturn, response, true, _1, _2))); |
| |
| int errno_ignored; |
| EXPECT_TRUE(scan_utils_.Scan(kFakeInterfaceIndex, false, |
| IWifiScannerImpl::SCAN_TYPE_HIGH_ACCURACY, |
| {}, {}, &errno_ignored)); |
| } |
| |
| TEST_F(ScanUtilsTest, CanSendScanRequestForHighAccuracyScanWithRandomAddr) { |
| NL80211Packet response = CreateControlMessageAck(); |
| EXPECT_CALL( |
| netlink_manager_, |
| SendMessageAndGetResponses( |
| AllOf( |
| DoesNL80211PacketMatchCommand(NL80211_CMD_TRIGGER_SCAN), |
| DoesNL80211PacketHaveAttributeWithUint32Value( |
| NL80211_ATTR_SCAN_FLAGS, |
| static_cast<uint32_t>(NL80211_SCAN_FLAG_RANDOM_ADDR | |
| NL80211_SCAN_FLAG_HIGH_ACCURACY))), |
| _)). |
| WillOnce(Invoke(bind(AppendMessageAndReturn, response, true, _1, _2))); |
| |
| int errno_ignored; |
| EXPECT_TRUE(scan_utils_.Scan(kFakeInterfaceIndex, true, |
| IWifiScannerImpl::SCAN_TYPE_HIGH_ACCURACY, |
| {}, {}, &errno_ignored)); |
| } |
| |
| TEST_F(ScanUtilsTest, CanHandleScanRequestFailure) { |
| NL80211Packet response = CreateControlMessageError(kFakeErrorCode); |
| EXPECT_CALL( |
| netlink_manager_, |
| SendMessageAndGetResponses( |
| DoesNL80211PacketMatchCommand(NL80211_CMD_TRIGGER_SCAN), _)). |
| WillOnce(Invoke(bind( |
| AppendMessageAndReturn, response, true, _1, _2))); |
| int error_code; |
| EXPECT_FALSE(scan_utils_.Scan(kFakeInterfaceIndex, kFakeUseRandomMAC, |
| kFakeScanType, {}, {}, &error_code)); |
| EXPECT_EQ(kFakeErrorCode, error_code); |
| } |
| |
| TEST_F(ScanUtilsTest, CanSendSchedScanRequest) { |
| NL80211Packet response = CreateControlMessageAck(); |
| EXPECT_CALL( |
| netlink_manager_, |
| SendMessageAndGetResponses( |
| DoesNL80211PacketMatchCommand(NL80211_CMD_START_SCHED_SCAN), _)). |
| WillOnce(Invoke(bind( |
| AppendMessageAndReturn, response, true, _1, _2))); |
| int errno_ignored; |
| EXPECT_TRUE(scan_utils_.StartScheduledScan( |
| kFakeInterfaceIndex, |
| SchedScanIntervalSetting(), |
| kFake2gRssiThreshold, kFake5gRssiThreshold, |
| kFakeUseRandomMAC, kFakeRequestLowPower, {}, {}, {}, &errno_ignored)); |
| // TODO(b/34231420): Add validation of requested scan ssids, threshold, |
| // and frequencies. |
| } |
| |
| TEST_F(ScanUtilsTest, CanHandleSchedScanRequestFailure) { |
| NL80211Packet response = CreateControlMessageError(kFakeErrorCode); |
| EXPECT_CALL( |
| netlink_manager_, |
| SendMessageAndGetResponses( |
| DoesNL80211PacketMatchCommand(NL80211_CMD_START_SCHED_SCAN), _)). |
| WillOnce(Invoke(bind( |
| AppendMessageAndReturn, response, true, _1, _2))); |
| int error_code; |
| EXPECT_FALSE(scan_utils_.StartScheduledScan( |
| kFakeInterfaceIndex, |
| SchedScanIntervalSetting(), |
| kFake2gRssiThreshold, kFake5gRssiThreshold, |
| kFakeUseRandomMAC, kFakeRequestLowPower, {}, {}, {}, &error_code)); |
| EXPECT_EQ(kFakeErrorCode, error_code); |
| } |
| |
| TEST_F(ScanUtilsTest, CanSendSchedScanRequestForLowPowerScan) { |
| NL80211Packet response = CreateControlMessageAck(); |
| EXPECT_CALL( |
| netlink_manager_, |
| SendMessageAndGetResponses( |
| AllOf( |
| DoesNL80211PacketMatchCommand(NL80211_CMD_START_SCHED_SCAN), |
| DoesNL80211PacketHaveAttributeWithUint32Value( |
| NL80211_ATTR_SCAN_FLAGS, NL80211_SCAN_FLAG_LOW_POWER)), |
| _)); |
| int errno_ignored; |
| scan_utils_.StartScheduledScan( |
| kFakeInterfaceIndex, |
| SchedScanIntervalSetting(), |
| kFake2gRssiThreshold, kFake5gRssiThreshold, |
| false, true, {}, {}, {}, &errno_ignored); |
| } |
| |
| TEST_F(ScanUtilsTest, CanSpecifyScanPlansForSchedScanRequest) { |
| EXPECT_CALL( |
| netlink_manager_, |
| SendMessageAndGetResponses( |
| AllOf( |
| DoesNL80211PacketMatchCommand(NL80211_CMD_START_SCHED_SCAN), |
| DoesNL80211PacketHaveAttribute(NL80211_ATTR_SCHED_SCAN_PLANS), |
| Not(DoesNL80211PacketHaveAttribute( |
| NL80211_ATTR_SCHED_SCAN_INTERVAL))), |
| _)); |
| int errno_ignored; |
| SchedScanIntervalSetting interval_setting{ |
| {{kFakeScheduledScanIntervalMs, 10 /* repeated times */}}, |
| kFakeScheduledScanIntervalMs * 3 /* interval for infinite scans */}; |
| |
| scan_utils_.StartScheduledScan( |
| kFakeInterfaceIndex, |
| interval_setting, |
| kFake2gRssiThreshold, kFake5gRssiThreshold, |
| kFakeUseRandomMAC, kFakeRequestLowPower, {}, {}, {}, &errno_ignored); |
| } |
| |
| TEST_F(ScanUtilsTest, CanSpecifySingleIntervalForSchedScanRequest) { |
| EXPECT_CALL( |
| netlink_manager_, |
| SendMessageAndGetResponses( |
| AllOf( |
| DoesNL80211PacketMatchCommand(NL80211_CMD_START_SCHED_SCAN), |
| DoesNL80211PacketHaveAttribute(NL80211_ATTR_SCHED_SCAN_INTERVAL), |
| Not(DoesNL80211PacketHaveAttribute( |
| NL80211_ATTR_SCHED_SCAN_PLANS))), |
| _)); |
| int errno_ignored; |
| SchedScanIntervalSetting interval_setting{{}, kFakeScheduledScanIntervalMs}; |
| |
| scan_utils_.StartScheduledScan( |
| kFakeInterfaceIndex, |
| interval_setting, |
| kFake2gRssiThreshold, kFake5gRssiThreshold, |
| kFakeUseRandomMAC, kFakeRequestLowPower, {}, {}, {}, &errno_ignored); |
| } |
| |
| TEST_F(ScanUtilsTest, CanPrioritizeLastSeenSinceBootNetlinkAttribute) { |
| constexpr uint64_t kLastSeenTimestampNanoSeconds = 123456; |
| constexpr uint64_t kBssTsfTimestampMicroSeconds = 654321; |
| NL80211NestedAttr bss(NL80211_ATTR_BSS); |
| bss.AddAttribute( |
| NL80211Attr<uint64_t>(NL80211_BSS_LAST_SEEN_BOOTTIME, |
| kLastSeenTimestampNanoSeconds)); |
| bss.AddAttribute( |
| NL80211Attr<uint64_t>(NL80211_BSS_TSF, kBssTsfTimestampMicroSeconds)); |
| uint64_t timestamp_microseconds; |
| EXPECT_TRUE(scan_utils_.GetBssTimestampForTesting( |
| bss, ×tamp_microseconds)); |
| EXPECT_EQ(kLastSeenTimestampNanoSeconds/1000, timestamp_microseconds); |
| } |
| |
| TEST_F(ScanUtilsTest, CanHandleMissingLastSeenSinceBootNetlinkAttribute) { |
| constexpr uint64_t kBssTsfTimestampMicroSeconds = 654321; |
| NL80211NestedAttr bss(NL80211_ATTR_BSS); |
| bss.AddAttribute( |
| NL80211Attr<uint64_t>(NL80211_BSS_TSF, kBssTsfTimestampMicroSeconds)); |
| uint64_t timestamp_microseconds; |
| EXPECT_TRUE(scan_utils_.GetBssTimestampForTesting( |
| bss, ×tamp_microseconds)); |
| EXPECT_EQ(kBssTsfTimestampMicroSeconds, timestamp_microseconds); |
| } |
| |
| // Probe TSF is newer. |
| TEST_F(ScanUtilsTest, CanPickMostRecentTimestampBetweenBetweenProbeAndBeacon1) { |
| constexpr uint64_t kBssBeaconTsfTimestampMicroSeconds = 654321; |
| constexpr uint64_t kBssTsfTimestampMicroSeconds = |
| kBssBeaconTsfTimestampMicroSeconds + 2000; |
| NL80211NestedAttr bss(NL80211_ATTR_BSS); |
| bss.AddAttribute( |
| NL80211Attr<uint64_t>(NL80211_BSS_BEACON_TSF, |
| kBssBeaconTsfTimestampMicroSeconds)); |
| bss.AddAttribute( |
| NL80211Attr<uint64_t>(NL80211_BSS_TSF, kBssTsfTimestampMicroSeconds)); |
| uint64_t timestamp_microseconds; |
| EXPECT_TRUE(scan_utils_.GetBssTimestampForTesting( |
| bss, ×tamp_microseconds)); |
| EXPECT_EQ(kBssTsfTimestampMicroSeconds, timestamp_microseconds); |
| } |
| |
| // Beacon TSF is newer. |
| TEST_F(ScanUtilsTest, CanPickMostRecentTimestampBetweenBetweenProbeAndBeacon2) { |
| constexpr uint64_t kBssTsfTimestampMicroSeconds = 654321; |
| constexpr uint64_t kBssBeaconTsfTimestampMicroSeconds = |
| kBssTsfTimestampMicroSeconds + 2000; |
| NL80211NestedAttr bss(NL80211_ATTR_BSS); |
| bss.AddAttribute( |
| NL80211Attr<uint64_t>(NL80211_BSS_BEACON_TSF, |
| kBssBeaconTsfTimestampMicroSeconds)); |
| bss.AddAttribute( |
| NL80211Attr<uint64_t>(NL80211_BSS_TSF, kBssTsfTimestampMicroSeconds)); |
| uint64_t timestamp_microseconds; |
| EXPECT_TRUE(scan_utils_.GetBssTimestampForTesting( |
| bss, ×tamp_microseconds)); |
| EXPECT_EQ(kBssBeaconTsfTimestampMicroSeconds, timestamp_microseconds); |
| } |
| |
| } // namespace wificond |
| } // namespace android |