| /* |
| * Copyright (C) 2022 The Android Open Source Project |
| * |
| * Licensed under the Staache 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 <cctype> |
| #include <vector> |
| |
| #include <VtsCoreUtil.h> |
| #include <aidl/Gtest.h> |
| #include <aidl/Vintf.h> |
| #include <aidl/android/hardware/wifi/BnWifi.h> |
| #include <android/binder_manager.h> |
| #include <android/binder_status.h> |
| #include <binder/IServiceManager.h> |
| #include <binder/ProcessState.h> |
| #include <cutils/properties.h> |
| |
| #include "wifi_aidl_test_utils.h" |
| |
| using aidl::android::hardware::wifi::IWifi; |
| using aidl::android::hardware::wifi::IWifiStaIface; |
| using aidl::android::hardware::wifi::MacAddress; |
| using aidl::android::hardware::wifi::Ssid; |
| using aidl::android::hardware::wifi::StaApfPacketFilterCapabilities; |
| using aidl::android::hardware::wifi::StaBackgroundScanCapabilities; |
| using aidl::android::hardware::wifi::StaLinkLayerStats; |
| using aidl::android::hardware::wifi::StaRoamingCapabilities; |
| using aidl::android::hardware::wifi::StaRoamingConfig; |
| using aidl::android::hardware::wifi::StaRoamingState; |
| using aidl::android::hardware::wifi::WifiBand; |
| using aidl::android::hardware::wifi::WifiDebugRxPacketFateReport; |
| using aidl::android::hardware::wifi::WifiDebugTxPacketFateReport; |
| using aidl::android::hardware::wifi::WifiStatusCode; |
| |
| class WifiStaIfaceAidlTest : public testing::TestWithParam<std::string> { |
| public: |
| void SetUp() override { |
| stopWifiService(getInstanceName()); |
| wifi_sta_iface_ = getWifiStaIface(getInstanceName()); |
| ASSERT_NE(nullptr, wifi_sta_iface_.get()); |
| } |
| |
| void TearDown() override { stopWifiService(getInstanceName()); } |
| |
| protected: |
| bool isFeatureSupported(IWifiStaIface::FeatureSetMask expected) { |
| int32_t features = 0; |
| EXPECT_TRUE(wifi_sta_iface_->getFeatureSet(&features).isOk()); |
| return features & static_cast<int32_t>(expected); |
| } |
| |
| ndk::ScopedAStatus createStaIface(std::shared_ptr<IWifiStaIface>* sta_iface) { |
| std::shared_ptr<IWifiChip> wifi_chip = getWifiChip(getInstanceName()); |
| EXPECT_NE(nullptr, wifi_chip.get()); |
| return wifi_chip->createStaIface(sta_iface); |
| } |
| |
| std::shared_ptr<IWifiStaIface> wifi_sta_iface_; |
| |
| // Checks if the mDNS Offload is supported by any NIC. |
| bool isMdnsOffloadPresentInNIC() { |
| return testing::deviceSupportsFeature("com.google.android.tv.mdns_offload"); |
| } |
| |
| bool doesDeviceSupportFullNetworkingUnder2w() { |
| return testing::deviceSupportsFeature("com.google.android.tv.full_networking_under_2w"); |
| } |
| |
| // Detect TV devices. |
| bool isTvDevice() { |
| return testing::deviceSupportsFeature("android.software.leanback") || |
| testing::deviceSupportsFeature("android.hardware.type.television"); |
| } |
| |
| // Detect Panel TV devices by using ro.oem.key1 property. |
| // https://docs.partner.android.com/tv/build/platform/props-vars/ro-oem-key1 |
| bool isPanelTvDevice() { |
| const std::string oem_key1 = getPropertyString("ro.oem.key1"); |
| if (oem_key1.size() < 9) { |
| return false; |
| } |
| if (oem_key1.substr(0, 3) != "ATV") { |
| return false; |
| } |
| const std::string psz_string = oem_key1.substr(6, 3); |
| // If PSZ string contains non digit, then it is not a panel TV device. |
| for (char ch : psz_string) { |
| if (!isdigit(ch)) { |
| return false; |
| } |
| } |
| // If PSZ is "000", then it is not a panel TV device. |
| if (psz_string == "000") { |
| return false; |
| } |
| return true; |
| } |
| |
| std::string getPropertyString(const char* property_name) { |
| char property_string_raw_bytes[PROPERTY_VALUE_MAX] = {}; |
| int len = property_get(property_name, property_string_raw_bytes, ""); |
| return std::string(property_string_raw_bytes, len); |
| } |
| |
| private: |
| const char* getInstanceName() { return GetParam().c_str(); } |
| }; |
| |
| /* |
| * GetFactoryMacAddress |
| * Ensures that calls to getFactoryMacAddress will retrieve a non-zero MAC. |
| */ |
| TEST_P(WifiStaIfaceAidlTest, GetFactoryMacAddress) { |
| std::array<uint8_t, 6> mac; |
| EXPECT_TRUE(wifi_sta_iface_->getFactoryMacAddress(&mac).isOk()); |
| std::array<uint8_t, 6> all_zero_mac = {0, 0, 0, 0, 0, 0}; |
| EXPECT_NE(mac, all_zero_mac); |
| } |
| |
| /* |
| * GetFeatureSet |
| */ |
| TEST_P(WifiStaIfaceAidlTest, GetFeatureSet) { |
| int32_t features = 0; |
| EXPECT_TRUE(wifi_sta_iface_->getFeatureSet(&features).isOk()); |
| EXPECT_NE(features, 0); |
| } |
| |
| /* |
| * CheckApfIsSupported: |
| * Ensures the APF packet filter is fully supported as required in VSR 14: |
| * https://docs.partner.android.com/gms/policies/vsr/vsr-14 |
| */ |
| // @VsrTest = 5.3.12 |
| TEST_P(WifiStaIfaceAidlTest, CheckApfIsSupported) { |
| const std::string oem_key1 = getPropertyString("ro.oem.key1"); |
| if (isTvDevice()) { |
| // Flat panel TV devices that support MDNS offload do not have to implement APF if the WiFi |
| // chipset does not have sufficient RAM to do so. |
| if (isPanelTvDevice() && isMdnsOffloadPresentInNIC()) { |
| GTEST_SKIP() << "Panel TV supports mDNS offload. It is not required to support APF"; |
| } |
| // For TV devices declaring the |
| // com.google.android.tv.full_networking_under_2w feature, this indicates |
| // the device can meet the <= 2W standby power requirement while |
| // continuously processing network packets on the CPU, even in standby mode. |
| // In these cases, APF support is strongly recommended rather than being |
| // mandatory. |
| if (doesDeviceSupportFullNetworkingUnder2w()) { |
| GTEST_SKIP() << "TV Device meets the <= 2W standby power demand requirement. It is not " |
| "required to support APF."; |
| } |
| } |
| int vendor_api_level = property_get_int32("ro.vendor.api_level", 0); |
| // Before VSR 14, APF support is optional. |
| if (vendor_api_level < __ANDROID_API_U__) { |
| if (!isFeatureSupported(IWifiStaIface::FeatureSetMask::APF)) { |
| GTEST_SKIP() << "APF packet filter capabilities are not supported."; |
| } |
| StaApfPacketFilterCapabilities apf_caps = {}; |
| EXPECT_TRUE(wifi_sta_iface_->getApfPacketFilterCapabilities(&apf_caps).isOk()); |
| return; |
| } |
| |
| EXPECT_TRUE(isFeatureSupported(IWifiStaIface::FeatureSetMask::APF)); |
| StaApfPacketFilterCapabilities apf_caps = {}; |
| EXPECT_TRUE(wifi_sta_iface_->getApfPacketFilterCapabilities(&apf_caps).isOk()); |
| EXPECT_GE(apf_caps.version, 4); |
| // Based on VSR-14 the usable memory must be at least 1024 bytes. |
| EXPECT_GE(apf_caps.maxLength, 1024); |
| if (vendor_api_level >= __ANDROID_API_V__) { |
| // Based on VSR-15 the usable memory must be at least 2000 bytes. |
| EXPECT_GE(apf_caps.maxLength, 2000); |
| } |
| } |
| |
| /* |
| * GetBackgroundScanCapabilities |
| */ |
| TEST_P(WifiStaIfaceAidlTest, GetBackgroundScanCapabilities) { |
| if (!isFeatureSupported(IWifiStaIface::FeatureSetMask::BACKGROUND_SCAN)) { |
| GTEST_SKIP() << "Background scan capabilities are not supported."; |
| } |
| StaBackgroundScanCapabilities caps = {}; |
| EXPECT_TRUE(wifi_sta_iface_->getBackgroundScanCapabilities(&caps).isOk()); |
| } |
| |
| /* |
| * GetLinkLayerStats |
| * Ensures that calls to getLinkLayerStats will retrieve a non-empty |
| * StaLinkLayerStats after link layer stats collection is enabled. |
| */ |
| TEST_P(WifiStaIfaceAidlTest, GetLinkLayerStats) { |
| if (!isFeatureSupported(IWifiStaIface::FeatureSetMask::LINK_LAYER_STATS)) { |
| GTEST_SKIP() << "Skipping this test since link layer stats are not supported."; |
| } |
| |
| // Enable link layer stats collection. |
| EXPECT_TRUE(wifi_sta_iface_->enableLinkLayerStatsCollection(true).isOk()); |
| |
| // Retrieve link layer stats. |
| StaLinkLayerStats link_layer_stats = {}; |
| EXPECT_TRUE(wifi_sta_iface_->getLinkLayerStats(&link_layer_stats).isOk()); |
| EXPECT_GT(link_layer_stats.timeStampInMs, 0); |
| |
| // Try to create a 2nd iface. If successful, it should fill the duty cycle field. |
| std::shared_ptr<IWifiStaIface> iface; |
| auto status = createStaIface(&iface); |
| if (status.isOk()) { |
| EXPECT_GT(link_layer_stats.iface.links[0].timeSliceDutyCycleInPercent, 0); |
| } |
| |
| // Disable link layer stats collection. |
| EXPECT_TRUE(wifi_sta_iface_->disableLinkLayerStatsCollection().isOk()); |
| } |
| |
| /* |
| * SetMacAddress |
| * Ensures that calls to setMacAddress will return successfully. |
| */ |
| TEST_P(WifiStaIfaceAidlTest, SetMacAddress) { |
| std::array<uint8_t, 6> mac = {0x12, 0x22, 0x33, 0x52, 0x10, 0x41}; |
| EXPECT_TRUE(wifi_sta_iface_->setMacAddress(mac).isOk()); |
| } |
| |
| /* |
| * SetScanMode |
| */ |
| TEST_P(WifiStaIfaceAidlTest, SetScanMode) { |
| auto status = wifi_sta_iface_->setScanMode(true); |
| EXPECT_TRUE(status.isOk() || checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED)); |
| |
| status = wifi_sta_iface_->setScanMode(false); |
| EXPECT_TRUE(status.isOk() || checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED)); |
| } |
| |
| /* |
| * LinkLayerStatsCollection |
| */ |
| TEST_P(WifiStaIfaceAidlTest, LinkLayerStatsCollection) { |
| if (!isFeatureSupported(IWifiStaIface::FeatureSetMask::LINK_LAYER_STATS)) { |
| GTEST_SKIP() << "Link layer stats collection is not supported."; |
| } |
| |
| // Enable link layer stats collection. |
| EXPECT_TRUE(wifi_sta_iface_->enableLinkLayerStatsCollection(true).isOk()); |
| |
| // Retrieve link layer stats. |
| StaLinkLayerStats link_layer_stats = {}; |
| EXPECT_TRUE(wifi_sta_iface_->getLinkLayerStats(&link_layer_stats).isOk()); |
| |
| // Disable link layer stats collection. |
| EXPECT_TRUE(wifi_sta_iface_->disableLinkLayerStatsCollection().isOk()); |
| } |
| |
| /* |
| * RSSIMonitoring |
| * Ensures that calls to startRssiMonitoring and stopRssiMonitoring will fail |
| * if the device is not connected to an AP. |
| */ |
| TEST_P(WifiStaIfaceAidlTest, RSSIMonitoring) { |
| if (!isFeatureSupported(IWifiStaIface::FeatureSetMask::RSSI_MONITOR)) { |
| GTEST_SKIP() << "RSSI monitoring is not supported."; |
| } |
| |
| const int cmd = 1; |
| const int maxRssi = -50; |
| const int minRssi = -90; |
| // Expected to fail because device is not connected to an AP. |
| EXPECT_FALSE(wifi_sta_iface_->startRssiMonitoring(cmd, maxRssi, minRssi).isOk()); |
| EXPECT_FALSE(wifi_sta_iface_->stopRssiMonitoring(cmd).isOk()); |
| } |
| |
| /* |
| * RoamingControl |
| */ |
| TEST_P(WifiStaIfaceAidlTest, RoamingControl) { |
| if (!isFeatureSupported(IWifiStaIface::FeatureSetMask::CONTROL_ROAMING)) { |
| GTEST_SKIP() << "Roaming control is not supported."; |
| } |
| |
| // Retrieve roaming capabilities. |
| StaRoamingCapabilities caps = {}; |
| EXPECT_TRUE(wifi_sta_iface_->getRoamingCapabilities(&caps).isOk()); |
| |
| // Set up roaming configuration based on roaming capabilities. |
| StaRoamingConfig roaming_config = {}; |
| if (caps.maxBlocklistSize > 0) { |
| MacAddress block_list_entry; |
| block_list_entry.data = std::array<uint8_t, 6>{{0x11, 0x22, 0x33, 0x44, 0x55, 0x66}}; |
| roaming_config.bssidBlocklist = {block_list_entry}; |
| } |
| if (caps.maxAllowlistSize > 0) { |
| Ssid allow_list_entry = {}; |
| allow_list_entry.data = std::array<uint8_t, 32>{{0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC}}; |
| roaming_config.ssidAllowlist = {allow_list_entry}; |
| } |
| |
| // Configure roaming. |
| EXPECT_TRUE(wifi_sta_iface_->configureRoaming(roaming_config).isOk()); |
| |
| // Enable roaming. |
| EXPECT_TRUE(wifi_sta_iface_->setRoamingState(StaRoamingState::ENABLED).isOk()); |
| } |
| |
| /* |
| * EnableNDOffload |
| */ |
| TEST_P(WifiStaIfaceAidlTest, EnableNDOffload) { |
| if (!isFeatureSupported(IWifiStaIface::FeatureSetMask::ND_OFFLOAD)) { |
| GTEST_SKIP() << "ND offload is not supported."; |
| } |
| EXPECT_TRUE(wifi_sta_iface_->enableNdOffload(true).isOk()); |
| } |
| |
| /* |
| * PacketFateMonitoring |
| */ |
| TEST_P(WifiStaIfaceAidlTest, PacketFateMonitoring) { |
| // Start packet fate monitoring. |
| auto status = wifi_sta_iface_->startDebugPacketFateMonitoring(); |
| EXPECT_TRUE(status.isOk() || checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED)); |
| |
| // Retrieve packets. |
| if (status.isOk()) { |
| std::vector<WifiDebugRxPacketFateReport> rx_reports; |
| std::vector<WifiDebugTxPacketFateReport> tx_reports; |
| EXPECT_TRUE(wifi_sta_iface_->getDebugRxPacketFates(&rx_reports).isOk()); |
| EXPECT_TRUE(wifi_sta_iface_->getDebugTxPacketFates(&tx_reports).isOk()); |
| } |
| } |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WifiStaIfaceAidlTest); |
| INSTANTIATE_TEST_SUITE_P(WifiTest, WifiStaIfaceAidlTest, |
| testing::ValuesIn(android::getAidlHalInstanceNames(IWifi::descriptor)), |
| android::PrintInstanceNameToString); |
| |
| int main(int argc, char** argv) { |
| ::testing::InitGoogleTest(&argc, argv); |
| android::ProcessState::self()->setThreadPoolMaxThreadCount(1); |
| android::ProcessState::self()->startThreadPool(); |
| return RUN_ALL_TESTS(); |
| } |