wifi: Add STA + STA HIDL API's

Add the HIDL API's + shim + VTS tests.

Bug: 170305665
Test: atest VtsHalWifiV1_3TargetTest VtsHalWifiV1_5TargetTest
Change-Id: I26ada11aebfe8082bfda251cf7e6990e0aa08a06
diff --git a/wifi/1.3/vts/functional/wifi_sta_iface_hidl_test.cpp b/wifi/1.3/vts/functional/wifi_sta_iface_hidl_test.cpp
index 22efe08..78e6239 100644
--- a/wifi/1.3/vts/functional/wifi_sta_iface_hidl_test.cpp
+++ b/wifi/1.3/vts/functional/wifi_sta_iface_hidl_test.cpp
@@ -21,6 +21,7 @@
 
 #include <android/hardware/wifi/1.3/IWifi.h>
 #include <android/hardware/wifi/1.3/IWifiStaIface.h>
+#include <android/hardware/wifi/1.5/IWifiStaIface.h>
 #include <gtest/gtest.h>
 #include <hidl/GtestPrinter.h>
 #include <hidl/ServiceManagement.h>
@@ -88,7 +89,6 @@
         // No-op if link layer stats is not supported.
         return;
     }
-
     // Enable link layer stats collection.
     EXPECT_EQ(WifiStatusCode::SUCCESS,
               HIDL_INVOKE(wifi_sta_iface_, enableLinkLayerStatsCollection, true)
@@ -96,8 +96,17 @@
     // Retrieve link layer stats.
     const auto& status_and_stats =
         HIDL_INVOKE(wifi_sta_iface_, getLinkLayerStats_1_3);
-    EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_stats.first.code);
-    EXPECT_GT(status_and_stats.second.timeStampInMs, 0u);
+    sp<android::hardware::wifi::V1_5::IWifiStaIface> staIface1_5 =
+        android::hardware::wifi::V1_5::IWifiStaIface::castFrom(wifi_sta_iface_);
+    if (staIface1_5.get() == nullptr) {
+        EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_stats.first.code);
+        EXPECT_GT(status_and_stats.second.timeStampInMs, 0u);
+    } else {
+        // not supported on 1.5 HAL.
+        EXPECT_EQ(WifiStatusCode::ERROR_NOT_SUPPORTED,
+                  status_and_stats.first.code);
+    }
+
     // Disable link layer stats collection.
     EXPECT_EQ(
         WifiStatusCode::SUCCESS,
diff --git a/wifi/1.5/Android.bp b/wifi/1.5/Android.bp
index ecb0a35..5a62a3a 100644
--- a/wifi/1.5/Android.bp
+++ b/wifi/1.5/Android.bp
@@ -9,6 +9,7 @@
         "IWifiChip.hal",
         "IWifiNanIface.hal",
         "IWifiNanIfaceEventCallback.hal",
+        "IWifiStaIface.hal",
     ],
     interfaces: [
         "android.hardware.wifi@1.0",
diff --git a/wifi/1.5/IWifiChip.hal b/wifi/1.5/IWifiChip.hal
index 5243baf..dcc9279 100644
--- a/wifi/1.5/IWifiChip.hal
+++ b/wifi/1.5/IWifiChip.hal
@@ -36,6 +36,53 @@
     };
 
     /**
+     * When there are 2 or more simultaneous STA connections, this use case hint indicates what
+     * use-case is being enabled by the framework. This use case hint can be used by the firmware
+     * to modify various firmware configurations like:
+     *   - Allowed BSSIDs the firmware can choose for the initial connection/roaming attempts.
+     *   - Duty cycle to choose for the 2 STA connections if the radio is in MCC mode.
+     *   - Whether roaming, APF and other offloads needs to be enabled or not.
+     * Note:
+     *   - This will be invoked before an active wifi connection is established on the second
+     *     interface.
+     *   - This use-case hint is implicitly void when the second STA interface is brought down.
+     */
+    enum MultiStaUseCase : uint8_t {
+        /**
+         * Usage:
+         * - This will be sent down for make before break use-case.
+         * - Platform is trying to speculatively connect to a second network and evaluate it without
+         *  disrupting the primary connection.
+         * Requirements for Firmware:
+         * - Do not reduce the number of tx/rx chains of primary connection.
+         * - If using MCC, should set the MCC duty cycle of the primary connection to be higher than
+         *  the secondary connection (maybe 70/30 split).
+         * - Should pick the best BSSID for the secondary STA (disregard the chip mode) independent
+         *   of the primary STA:
+         *    - Don’t optimize for DBS vs MCC/SCC
+         * - Should not impact the primary connection’s bssid selection:
+         *    - Don’t downgrade chains of the existing primary connection.
+         *    - Don’t optimize for DBS vs MCC/SCC.
+         */
+        DUAL_STA_TRANSIENT_PREFER_PRIMARY = 0,
+        /**
+         * Usage:
+         * - This will be sent down for any app requested peer to peer connections.
+         * - In this case, both the connections needs to be allocated equal resources.
+         * - For the peer to peer use case, BSSID for the secondary connection will be chosen by the
+         *   framework.
+         *
+         * Requirements for Firmware:
+         * - Can choose MCC or DBS mode depending on the MCC efficiency and HW capability.
+         * - If using MCC, set the MCC duty cycle of the primary connection to be equal to the
+         *   secondary connection.
+         * - Prefer BSSID candidates which will help provide the best "overall" performance for both
+         *   the connections.
+         */
+        DUAL_STA_NON_TRANSIENT_UNBIASED = 1,
+    };
+
+    /**
      * Get the capabilities supported by this chip.
      *
      * @return status WifiStatus of the operation.
@@ -48,4 +95,36 @@
      */
     getCapabilities_1_5()
         generates (WifiStatus status, bitfield<ChipCapabilityMask> capabilities);
+
+    /**
+     * Invoked to indicate that the provided iface is the primary STA iface when there are more
+     * than 1 STA iface concurrently active.
+     * Note: If the wifi firmware/chip cannot support multiple instances of any offload
+     * (like roaming, APF, rssi threshold, etc), the firmware should ensure that these
+     * offloads are at least enabled for the primary interface. If the new primary interface is
+     * already connected to a network, the firmware must switch all the offloads on
+     * this new interface without disconnecting.
+     *
+     * @param ifname Name of the STA iface.
+     * @return status WifiStatus of the operation.
+     *         Possible status codes:
+     *         |WifiStatusCode.SUCCESS|,
+     *         |WifiStatusCode.ERROR_WIFI_CHIP_INVALID|,
+     *         |WifiStatusCode.ERROR_INVALID_ARGS|
+     */
+    setMultiStaPrimaryConnection(string ifName) generates (WifiStatus status);
+
+    /**
+     * Invoked to indicate the STA + STA use-case that is active.
+     *
+     * Refer to documentation of |MultiStaUseCase| for details.
+     *
+     * @param useCase Use case that is active.
+     * @return status WifiStatus of the operation.
+     *         Possible status codes:
+     *         |WifiStatusCode.SUCCESS|,
+     *         |WifiStatusCode.ERROR_WIFI_CHIP_INVALID|,
+     *         |WifiStatusCode.ERROR_INVALID_ARGS|
+     */
+    setMultiStaUseCase(MultiStaUseCase useCase) generates (WifiStatus status);
 };
diff --git a/wifi/1.5/IWifiNanIface.hal b/wifi/1.5/IWifiNanIface.hal
index 93257b5..d7c15de 100644
--- a/wifi/1.5/IWifiNanIface.hal
+++ b/wifi/1.5/IWifiNanIface.hal
@@ -92,13 +92,12 @@
      *         |WifiStatusCode.SUCCESS|,
      *         |WifiStatusCode.ERROR_WIFI_IFACE_INVALID|
      */
-    registerEventCallback_1_5(IWifiNanIfaceEventCallback callback)
-        generates (WifiStatus status);
+    registerEventCallback_1_5(IWifiNanIfaceEventCallback callback) generates (WifiStatus status);
 
     /**
      * Get NAN capabilities. Asynchronous response is with
      * |IWifiNanIfaceEventCallback.notifyCapabilitiesResponse|.
-
+     *
      * Note: supersedes the @1.0::IWifiNanIface.getCapabilitiesRequest() method which is deprecated
      * as of HAL version 1.5.
      *
@@ -109,5 +108,5 @@
      *         |WifiStatusCode.ERROR_WIFI_IFACE_INVALID|,
      *         |WifiStatusCode.ERROR_UNKNOWN|
      */
-     getCapabilitiesRequest_1_5(CommandIdShort cmdId) generates (WifiStatus status);
-};
\ No newline at end of file
+    getCapabilitiesRequest_1_5(CommandIdShort cmdId) generates (WifiStatus status);
+};
diff --git a/wifi/1.5/IWifiNanIfaceEventCallback.hal b/wifi/1.5/IWifiNanIfaceEventCallback.hal
index 867c03c..046b702 100644
--- a/wifi/1.5/IWifiNanIfaceEventCallback.hal
+++ b/wifi/1.5/IWifiNanIfaceEventCallback.hal
@@ -40,5 +40,5 @@
      * @param capabilities Capability data.
      */
     oneway notifyCapabilitiesResponse_1_5(CommandIdShort id, WifiNanStatus status,
-                                    NanCapabilities capabilities);
-};
\ No newline at end of file
+        NanCapabilities capabilities);
+};
diff --git a/wifi/1.5/IWifiStaIface.hal b/wifi/1.5/IWifiStaIface.hal
new file mode 100644
index 0000000..e9d411e
--- /dev/null
+++ b/wifi/1.5/IWifiStaIface.hal
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2020 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.
+ */
+
+package android.hardware.wifi@1.5;
+
+import @1.0::WifiStatus;
+import @1.3::IWifiStaIface;
+
+/**
+ * Interface used to represent a single STA iface.
+ *
+ * IWifiChip.createStaIface() must return a @1.5::IWifiStaIface when supported.
+ */
+interface IWifiStaIface extends @1.3::IWifiStaIface {
+    /**
+     * Retrieve the latest link layer stats.
+     * Must fail if |StaIfaceCapabilityMask.LINK_LAYER_STATS| is not set or if
+     * link layer stats collection hasn't been explicitly enabled.
+     *
+     * @return status WifiStatus of the operation.
+     *     Possible status codes:
+     *     |WifiStatusCode.SUCCESS|,
+     *     |WifiStatusCode.ERROR_WIFI_IFACE_INVALID|,
+     *     |WifiStatusCode.ERROR_NOT_SUPPORTED|,
+     *     |WifiStatusCode.ERROR_NOT_STARTED|,
+     *     |WifiStatusCode.ERROR_NOT_AVAILABLE|,
+     *     |WifiStatusCode.ERROR_UNKNOWN|
+     * @return stats Instance of |LinkLayerStats|.
+     */
+    getLinkLayerStats_1_5() generates (WifiStatus status, StaLinkLayerStats stats);
+};
diff --git a/wifi/1.5/default/hidl_struct_util.cpp b/wifi/1.5/default/hidl_struct_util.cpp
index 578f3e2..83d06fe 100644
--- a/wifi/1.5/default/hidl_struct_util.cpp
+++ b/wifi/1.5/default/hidl_struct_util.cpp
@@ -866,46 +866,48 @@
 
 bool convertLegacyLinkLayerStatsToHidl(
     const legacy_hal::LinkLayerStats& legacy_stats,
-    V1_3::StaLinkLayerStats* hidl_stats) {
+    StaLinkLayerStats* hidl_stats) {
     if (!hidl_stats) {
         return false;
     }
     *hidl_stats = {};
     // iface legacy_stats conversion.
-    hidl_stats->iface.beaconRx = legacy_stats.iface.beacon_rx;
-    hidl_stats->iface.avgRssiMgmt = legacy_stats.iface.rssi_mgmt;
-    hidl_stats->iface.wmeBePktStats.rxMpdu =
+    hidl_stats->iface.V1_0.beaconRx = legacy_stats.iface.beacon_rx;
+    hidl_stats->iface.V1_0.avgRssiMgmt = legacy_stats.iface.rssi_mgmt;
+    hidl_stats->iface.V1_0.wmeBePktStats.rxMpdu =
         legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].rx_mpdu;
-    hidl_stats->iface.wmeBePktStats.txMpdu =
+    hidl_stats->iface.V1_0.wmeBePktStats.txMpdu =
         legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].tx_mpdu;
-    hidl_stats->iface.wmeBePktStats.lostMpdu =
+    hidl_stats->iface.V1_0.wmeBePktStats.lostMpdu =
         legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].mpdu_lost;
-    hidl_stats->iface.wmeBePktStats.retries =
+    hidl_stats->iface.V1_0.wmeBePktStats.retries =
         legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].retries;
-    hidl_stats->iface.wmeBkPktStats.rxMpdu =
+    hidl_stats->iface.V1_0.wmeBkPktStats.rxMpdu =
         legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].rx_mpdu;
-    hidl_stats->iface.wmeBkPktStats.txMpdu =
+    hidl_stats->iface.V1_0.wmeBkPktStats.txMpdu =
         legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].tx_mpdu;
-    hidl_stats->iface.wmeBkPktStats.lostMpdu =
+    hidl_stats->iface.V1_0.wmeBkPktStats.lostMpdu =
         legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].mpdu_lost;
-    hidl_stats->iface.wmeBkPktStats.retries =
+    hidl_stats->iface.V1_0.wmeBkPktStats.retries =
         legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].retries;
-    hidl_stats->iface.wmeViPktStats.rxMpdu =
+    hidl_stats->iface.V1_0.wmeViPktStats.rxMpdu =
         legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].rx_mpdu;
-    hidl_stats->iface.wmeViPktStats.txMpdu =
+    hidl_stats->iface.V1_0.wmeViPktStats.txMpdu =
         legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].tx_mpdu;
-    hidl_stats->iface.wmeViPktStats.lostMpdu =
+    hidl_stats->iface.V1_0.wmeViPktStats.lostMpdu =
         legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].mpdu_lost;
-    hidl_stats->iface.wmeViPktStats.retries =
+    hidl_stats->iface.V1_0.wmeViPktStats.retries =
         legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].retries;
-    hidl_stats->iface.wmeVoPktStats.rxMpdu =
+    hidl_stats->iface.V1_0.wmeVoPktStats.rxMpdu =
         legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].rx_mpdu;
-    hidl_stats->iface.wmeVoPktStats.txMpdu =
+    hidl_stats->iface.V1_0.wmeVoPktStats.txMpdu =
         legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].tx_mpdu;
-    hidl_stats->iface.wmeVoPktStats.lostMpdu =
+    hidl_stats->iface.V1_0.wmeVoPktStats.lostMpdu =
         legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].mpdu_lost;
-    hidl_stats->iface.wmeVoPktStats.retries =
+    hidl_stats->iface.V1_0.wmeVoPktStats.retries =
         legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].retries;
+    hidl_stats->iface.timeSliceDutyCycleInPercent =
+        legacy_stats.iface.info.time_slicing_duty_cycle_percent;
     // radio legacy_stats conversion.
     std::vector<V1_3::StaLinkLayerRadioStats> hidl_radios_stats;
     for (const auto& legacy_radio_stats : legacy_stats.radios) {
@@ -2787,6 +2789,17 @@
     }
     CHECK(false);
 }
+
+legacy_hal::wifi_multi_sta_use_case convertHidlMultiStaUseCaseToLegacy(
+    IWifiChip::MultiStaUseCase use_case) {
+    switch (use_case) {
+        case IWifiChip::MultiStaUseCase::DUAL_STA_TRANSIENT_PREFER_PRIMARY:
+            return legacy_hal::WIFI_DUAL_STA_TRANSIENT_PREFER_PRIMARY;
+        case IWifiChip::MultiStaUseCase::DUAL_STA_NON_TRANSIENT_UNBIASED:
+            return legacy_hal::WIFI_DUAL_STA_NON_TRANSIENT_UNBIASED;
+    }
+    CHECK(false);
+}
 }  // namespace hidl_struct_util
 }  // namespace implementation
 }  // namespace V1_5
diff --git a/wifi/1.5/default/hidl_struct_util.h b/wifi/1.5/default/hidl_struct_util.h
index b0b1d22..49d8a12 100644
--- a/wifi/1.5/default/hidl_struct_util.h
+++ b/wifi/1.5/default/hidl_struct_util.h
@@ -69,6 +69,8 @@
         hidl_radio_mode_infos);
 legacy_hal::wifi_interface_type convertHidlIfaceTypeToLegacy(
     IfaceType hidl_interface_type);
+legacy_hal::wifi_multi_sta_use_case convertHidlMultiStaUseCaseToLegacy(
+    IWifiChip::MultiStaUseCase use_case);
 
 // STA iface conversion methods.
 bool convertLegacyFeaturesToHidlStaCapabilities(
@@ -96,7 +98,7 @@
     std::vector<StaScanData>* hidl_scan_datas);
 bool convertLegacyLinkLayerStatsToHidl(
     const legacy_hal::LinkLayerStats& legacy_stats,
-    V1_3::StaLinkLayerStats* hidl_stats);
+    StaLinkLayerStats* hidl_stats);
 bool convertLegacyRoamingCapabilitiesToHidl(
     const legacy_hal::wifi_roaming_capabilities& legacy_caps,
     StaRoamingCapabilities* hidl_caps);
diff --git a/wifi/1.5/default/tests/hidl_struct_util_unit_tests.cpp b/wifi/1.5/default/tests/hidl_struct_util_unit_tests.cpp
index 81eb14e..ea84c61 100644
--- a/wifi/1.5/default/tests/hidl_struct_util_unit_tests.cpp
+++ b/wifi/1.5/default/tests/hidl_struct_util_unit_tests.cpp
@@ -154,6 +154,8 @@
     legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].mpdu_lost = rand();
     legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].retries = rand();
 
+    legacy_stats.iface.info.time_slicing_duty_cycle_percent = rand();
+
     for (auto& radio : legacy_stats.radios) {
         radio.stats.on_time = rand();
         radio.stats.tx_time = rand();
@@ -182,46 +184,49 @@
         radio.channel_stats.push_back(channel_stat2);
     }
 
-    V1_3::StaLinkLayerStats converted{};
+    V1_5::StaLinkLayerStats converted{};
     hidl_struct_util::convertLegacyLinkLayerStatsToHidl(legacy_stats,
                                                         &converted);
-    EXPECT_EQ(legacy_stats.iface.beacon_rx, converted.iface.beaconRx);
-    EXPECT_EQ(legacy_stats.iface.rssi_mgmt, converted.iface.avgRssiMgmt);
+    EXPECT_EQ(legacy_stats.iface.beacon_rx, converted.iface.V1_0.beaconRx);
+    EXPECT_EQ(legacy_stats.iface.rssi_mgmt, converted.iface.V1_0.avgRssiMgmt);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].rx_mpdu,
-              converted.iface.wmeBePktStats.rxMpdu);
+              converted.iface.V1_0.wmeBePktStats.rxMpdu);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].tx_mpdu,
-              converted.iface.wmeBePktStats.txMpdu);
+              converted.iface.V1_0.wmeBePktStats.txMpdu);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].mpdu_lost,
-              converted.iface.wmeBePktStats.lostMpdu);
+              converted.iface.V1_0.wmeBePktStats.lostMpdu);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].retries,
-              converted.iface.wmeBePktStats.retries);
+              converted.iface.V1_0.wmeBePktStats.retries);
 
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].rx_mpdu,
-              converted.iface.wmeBkPktStats.rxMpdu);
+              converted.iface.V1_0.wmeBkPktStats.rxMpdu);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].tx_mpdu,
-              converted.iface.wmeBkPktStats.txMpdu);
+              converted.iface.V1_0.wmeBkPktStats.txMpdu);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].mpdu_lost,
-              converted.iface.wmeBkPktStats.lostMpdu);
+              converted.iface.V1_0.wmeBkPktStats.lostMpdu);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].retries,
-              converted.iface.wmeBkPktStats.retries);
+              converted.iface.V1_0.wmeBkPktStats.retries);
 
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].rx_mpdu,
-              converted.iface.wmeViPktStats.rxMpdu);
+              converted.iface.V1_0.wmeViPktStats.rxMpdu);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].tx_mpdu,
-              converted.iface.wmeViPktStats.txMpdu);
+              converted.iface.V1_0.wmeViPktStats.txMpdu);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].mpdu_lost,
-              converted.iface.wmeViPktStats.lostMpdu);
+              converted.iface.V1_0.wmeViPktStats.lostMpdu);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].retries,
-              converted.iface.wmeViPktStats.retries);
+              converted.iface.V1_0.wmeViPktStats.retries);
 
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].rx_mpdu,
-              converted.iface.wmeVoPktStats.rxMpdu);
+              converted.iface.V1_0.wmeVoPktStats.rxMpdu);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].tx_mpdu,
-              converted.iface.wmeVoPktStats.txMpdu);
+              converted.iface.V1_0.wmeVoPktStats.txMpdu);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].mpdu_lost,
-              converted.iface.wmeVoPktStats.lostMpdu);
+              converted.iface.V1_0.wmeVoPktStats.lostMpdu);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].retries,
-              converted.iface.wmeVoPktStats.retries);
+              converted.iface.V1_0.wmeVoPktStats.retries);
+
+    EXPECT_EQ(legacy_stats.iface.info.time_slicing_duty_cycle_percent,
+              converted.iface.timeSliceDutyCycleInPercent);
 
     EXPECT_EQ(legacy_stats.radios.size(), converted.radios.size());
     for (size_t i = 0; i < legacy_stats.radios.size(); i++) {
diff --git a/wifi/1.5/default/wifi_chip.cpp b/wifi/1.5/default/wifi_chip.cpp
index ebff722..1c238c8 100644
--- a/wifi/1.5/default/wifi_chip.cpp
+++ b/wifi/1.5/default/wifi_chip.cpp
@@ -678,6 +678,20 @@
                            hidl_status_cb, event_callback);
 }
 
+Return<void> WifiChip::setMultiStaPrimaryConnection(
+    const hidl_string& ifname, setMultiStaPrimaryConnection_cb hidl_status_cb) {
+    return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+                           &WifiChip::setMultiStaPrimaryConnectionInternal,
+                           hidl_status_cb, ifname);
+}
+
+Return<void> WifiChip::setMultiStaUseCase(
+    MultiStaUseCase use_case, setMultiStaUseCase_cb hidl_status_cb) {
+    return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+                           &WifiChip::setMultiStaUseCaseInternal,
+                           hidl_status_cb, use_case);
+}
+
 void WifiChip::invalidateAndRemoveAllIfaces() {
     invalidateAndClearAll(ap_ifaces_);
     invalidateAndClearAll(nan_ifaces_);
@@ -1016,7 +1030,7 @@
     return createWifiStatus(WifiStatusCode::SUCCESS);
 }
 
-std::pair<WifiStatus, sp<V1_3::IWifiStaIface>>
+std::pair<WifiStatus, sp<V1_5::IWifiStaIface>>
 WifiChip::createStaIfaceInternal() {
     if (!canCurrentModeSupportIfaceOfTypeWithCurrentIfaces(IfaceType::STA)) {
         return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
@@ -1050,7 +1064,7 @@
     return {createWifiStatus(WifiStatusCode::SUCCESS), getNames(sta_ifaces_)};
 }
 
-std::pair<WifiStatus, sp<V1_3::IWifiStaIface>> WifiChip::getStaIfaceInternal(
+std::pair<WifiStatus, sp<V1_5::IWifiStaIface>> WifiChip::getStaIfaceInternal(
     const std::string& ifname) {
     const auto iface = findUsingName(sta_ifaces_, ifname);
     if (!iface.get()) {
@@ -1295,6 +1309,19 @@
     return createWifiStatus(WifiStatusCode::SUCCESS);
 }
 
+WifiStatus WifiChip::setMultiStaPrimaryConnectionInternal(
+    const std::string& ifname) {
+    auto legacy_status =
+        legacy_hal_.lock()->multiStaSetPrimaryConnection(ifname);
+    return createWifiStatusFromLegacyError(legacy_status);
+}
+
+WifiStatus WifiChip::setMultiStaUseCaseInternal(MultiStaUseCase use_case) {
+    auto legacy_status = legacy_hal_.lock()->multiStaSetUseCase(
+        hidl_struct_util::convertHidlMultiStaUseCaseToLegacy(use_case));
+    return createWifiStatusFromLegacyError(legacy_status);
+}
+
 WifiStatus WifiChip::handleChipConfiguration(
     /* NONNULL */ std::unique_lock<std::recursive_mutex>* lock,
     ChipModeId mode_id) {
diff --git a/wifi/1.5/default/wifi_chip.h b/wifi/1.5/default/wifi_chip.h
index 8cc0452..693d480 100644
--- a/wifi/1.5/default/wifi_chip.h
+++ b/wifi/1.5/default/wifi_chip.h
@@ -163,6 +163,12 @@
     Return<void> registerEventCallback_1_4(
         const sp<V1_4::IWifiChipEventCallback>& event_callback,
         registerEventCallback_1_4_cb hidl_status_cb) override;
+    Return<void> setMultiStaPrimaryConnection(
+        const hidl_string& ifname,
+        setMultiStaPrimaryConnection_cb hidl_status_cb) override;
+    Return<void> setMultiStaUseCase(
+        MultiStaUseCase use_case,
+        setMultiStaUseCase_cb hidl_status_cb) override;
 
    private:
     void invalidateAndRemoveAllIfaces();
@@ -201,9 +207,9 @@
     std::pair<WifiStatus, sp<IWifiP2pIface>> getP2pIfaceInternal(
         const std::string& ifname);
     WifiStatus removeP2pIfaceInternal(const std::string& ifname);
-    std::pair<WifiStatus, sp<V1_3::IWifiStaIface>> createStaIfaceInternal();
+    std::pair<WifiStatus, sp<V1_5::IWifiStaIface>> createStaIfaceInternal();
     std::pair<WifiStatus, std::vector<hidl_string>> getStaIfaceNamesInternal();
-    std::pair<WifiStatus, sp<V1_3::IWifiStaIface>> getStaIfaceInternal(
+    std::pair<WifiStatus, sp<V1_5::IWifiStaIface>> getStaIfaceInternal(
         const std::string& ifname);
     WifiStatus removeStaIfaceInternal(const std::string& ifname);
     std::pair<WifiStatus, sp<V1_0::IWifiRttController>>
@@ -233,6 +239,8 @@
     createRttControllerInternal_1_4(const sp<IWifiIface>& bound_iface);
     WifiStatus registerEventCallbackInternal_1_4(
         const sp<V1_4::IWifiChipEventCallback>& event_callback);
+    WifiStatus setMultiStaPrimaryConnectionInternal(const std::string& ifname);
+    WifiStatus setMultiStaUseCaseInternal(MultiStaUseCase use_case);
 
     WifiStatus handleChipConfiguration(
         std::unique_lock<std::recursive_mutex>* lock, ChipModeId mode_id);
diff --git a/wifi/1.5/default/wifi_legacy_hal.cpp b/wifi/1.5/default/wifi_legacy_hal.cpp
index 398bfac..76e718b 100644
--- a/wifi/1.5/default/wifi_legacy_hal.cpp
+++ b/wifi/1.5/default/wifi_legacy_hal.cpp
@@ -1510,6 +1510,17 @@
     return res;
 }
 
+wifi_error WifiLegacyHal::multiStaSetPrimaryConnection(
+    const std::string& ifname) {
+    return global_func_table_.wifi_multi_sta_set_primary_connection(
+        global_handle_, getIfaceHandle(ifname));
+}
+
+wifi_error WifiLegacyHal::multiStaSetUseCase(wifi_multi_sta_use_case use_case) {
+    return global_func_table_.wifi_multi_sta_set_use_case(global_handle_,
+                                                          use_case);
+}
+
 void WifiLegacyHal::invalidate() {
     global_handle_ = nullptr;
     iface_name_to_handle_.clear();
diff --git a/wifi/1.5/default/wifi_legacy_hal.h b/wifi/1.5/default/wifi_legacy_hal.h
index 9a06efd..555c540 100644
--- a/wifi/1.5/default/wifi_legacy_hal.h
+++ b/wifi/1.5/default/wifi_legacy_hal.h
@@ -382,6 +382,10 @@
     virtual wifi_error deleteVirtualInterface(const std::string& ifname);
     wifi_error getSupportedIfaceName(uint32_t iface_type, std::string& ifname);
 
+    // STA + STA functions
+    virtual wifi_error multiStaSetPrimaryConnection(const std::string& ifname);
+    virtual wifi_error multiStaSetUseCase(wifi_multi_sta_use_case use_case);
+
    private:
     // Retrieve interface handles for all the available interfaces.
     wifi_error retrieveIfaceHandles();
diff --git a/wifi/1.5/default/wifi_legacy_hal_stubs.cpp b/wifi/1.5/default/wifi_legacy_hal_stubs.cpp
index a1122e9..71d2ddf 100644
--- a/wifi/1.5/default/wifi_legacy_hal_stubs.cpp
+++ b/wifi/1.5/default/wifi_legacy_hal_stubs.cpp
@@ -147,6 +147,8 @@
     populateStubFor(&hal_fn->wifi_get_supported_iface_name);
     populateStubFor(&hal_fn->wifi_early_initialize);
     populateStubFor(&hal_fn->wifi_get_chip_feature_set);
+    populateStubFor(&hal_fn->wifi_multi_sta_set_primary_connection);
+    populateStubFor(&hal_fn->wifi_multi_sta_set_use_case);
 
     return true;
 }
diff --git a/wifi/1.5/default/wifi_sta_iface.cpp b/wifi/1.5/default/wifi_sta_iface.cpp
index 04087fd..f3dcfc5 100644
--- a/wifi/1.5/default/wifi_sta_iface.cpp
+++ b/wifi/1.5/default/wifi_sta_iface.cpp
@@ -163,6 +163,13 @@
                            hidl_status_cb);
 }
 
+Return<void> WifiStaIface::getLinkLayerStats_1_5(
+    getLinkLayerStats_1_5_cb hidl_status_cb) {
+    return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+                           &WifiStaIface::getLinkLayerStatsInternal_1_5,
+                           hidl_status_cb);
+}
+
 Return<void> WifiStaIface::startRssiMonitoring(
     uint32_t cmd_id, int32_t max_rssi, int32_t min_rssi,
     startRssiMonitoring_cb hidl_status_cb) {
@@ -470,6 +477,11 @@
 
 std::pair<WifiStatus, V1_3::StaLinkLayerStats>
 WifiStaIface::getLinkLayerStatsInternal_1_3() {
+    return {createWifiStatus(WifiStatusCode::ERROR_NOT_SUPPORTED), {}};
+}
+
+std::pair<WifiStatus, V1_5::StaLinkLayerStats>
+WifiStaIface::getLinkLayerStatsInternal_1_5() {
     legacy_hal::wifi_error legacy_status;
     legacy_hal::LinkLayerStats legacy_stats;
     std::tie(legacy_status, legacy_stats) =
@@ -477,7 +489,7 @@
     if (legacy_status != legacy_hal::WIFI_SUCCESS) {
         return {createWifiStatusFromLegacyError(legacy_status), {}};
     }
-    V1_3::StaLinkLayerStats hidl_stats;
+    V1_5::StaLinkLayerStats hidl_stats;
     if (!hidl_struct_util::convertLegacyLinkLayerStatsToHidl(legacy_stats,
                                                              &hidl_stats)) {
         return {createWifiStatus(WifiStatusCode::ERROR_UNKNOWN), {}};
diff --git a/wifi/1.5/default/wifi_sta_iface.h b/wifi/1.5/default/wifi_sta_iface.h
index 7695f3c..94873c9 100644
--- a/wifi/1.5/default/wifi_sta_iface.h
+++ b/wifi/1.5/default/wifi_sta_iface.h
@@ -19,7 +19,7 @@
 
 #include <android-base/macros.h>
 #include <android/hardware/wifi/1.0/IWifiStaIfaceEventCallback.h>
-#include <android/hardware/wifi/1.3/IWifiStaIface.h>
+#include <android/hardware/wifi/1.5/IWifiStaIface.h>
 
 #include "hidl_callback_util.h"
 #include "wifi_iface_util.h"
@@ -35,7 +35,7 @@
 /**
  * HIDL interface object used to control a STA Iface instance.
  */
-class WifiStaIface : public V1_3::IWifiStaIface {
+class WifiStaIface : public V1_5::IWifiStaIface {
    public:
     WifiStaIface(const std::string& ifname,
                  const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
@@ -78,6 +78,8 @@
         getLinkLayerStats_cb hidl_status_cb) override;
     Return<void> getLinkLayerStats_1_3(
         getLinkLayerStats_1_3_cb hidl_status_cb) override;
+    Return<void> getLinkLayerStats_1_5(
+        getLinkLayerStats_1_5_cb hidl_status_cb) override;
     Return<void> startRssiMonitoring(
         uint32_t cmd_id, int32_t max_rssi, int32_t min_rssi,
         startRssiMonitoring_cb hidl_status_cb) override;
@@ -138,6 +140,8 @@
     std::pair<WifiStatus, V1_0::StaLinkLayerStats> getLinkLayerStatsInternal();
     std::pair<WifiStatus, V1_3::StaLinkLayerStats>
     getLinkLayerStatsInternal_1_3();
+    std::pair<WifiStatus, V1_5::StaLinkLayerStats>
+    getLinkLayerStatsInternal_1_5();
     WifiStatus startRssiMonitoringInternal(uint32_t cmd_id, int32_t max_rssi,
                                            int32_t min_rssi);
     WifiStatus stopRssiMonitoringInternal(uint32_t cmd_id);
diff --git a/wifi/1.5/types.hal b/wifi/1.5/types.hal
index 5873f79..3fbec82 100644
--- a/wifi/1.5/types.hal
+++ b/wifi/1.5/types.hal
@@ -16,10 +16,14 @@
 
 package android.hardware.wifi@1.5;
 
+import @1.0::StaLinkLayerIfaceStats;
+import @1.0::StaLinkLayerIfacePacketStats;
+import @1.0::TimeStampInMs;
 import @1.0::WifiBand;
 import @1.0::NanCipherSuiteType;
 import @1.0::NanCapabilities;
 import @1.2::NanConfigRequestSupplemental;
+import @1.3::StaLinkLayerRadioStats;
 
 /**
  * Wifi bands defined in 80211 spec.
@@ -48,6 +52,7 @@
      * Baseline information as defined in HAL 1.2.
      */
     @1.2::NanConfigRequestSupplemental V1_2;
+
     /**
      * Controls whether NAN instant communication mode is enabled.
      */
@@ -62,8 +67,43 @@
      * Baseline information as defined in HAL 1.0.
      */
     @1.0::NanCapabilities V1_0;
+
     /**
      * Flag to indicate id instant communication mode is supported.
      */
     bool instantCommunicationModeSupportFlag;
-};
\ No newline at end of file
+};
+
+/**
+ * Iface statistics for the current connection.
+ */
+struct StaLinkLayerIfaceStats {
+    /**
+     * Baseline information as defined in HAL 1.0.
+     */
+    @1.0::StaLinkLayerIfaceStats V1_0;
+
+    /**
+     * Duty cycle for the iface.
+     * if this iface is being served using time slicing on a radio with one or more ifaces
+     * (i.e MCC), then the duty cycle assigned to this iface in %.
+     * If not using time slicing (i.e SCC or DBS), set to 100.
+     */
+    uint8_t timeSliceDutyCycleInPercent;
+};
+
+/**
+ * Link layer stats retrieved via |getLinkLayerStats|.
+ */
+struct StaLinkLayerStats {
+    StaLinkLayerIfaceStats iface;
+
+    vec<StaLinkLayerRadioStats> radios;
+
+    /**
+     * TimeStamp for each stats sample.
+     * This is the absolute milliseconds from boot when these stats were
+     * sampled.
+     */
+    TimeStampInMs timeStampInMs;
+};
diff --git a/wifi/1.5/vts/functional/Android.bp b/wifi/1.5/vts/functional/Android.bp
index a7ba498..eb595c9 100644
--- a/wifi/1.5/vts/functional/Android.bp
+++ b/wifi/1.5/vts/functional/Android.bp
@@ -14,6 +14,29 @@
 // limitations under the License.
 //
 
+cc_test {
+    name: "VtsHalWifiV1_5TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: [
+        "wifi_chip_hidl_test.cpp",
+        "wifi_sta_iface_hidl_test.cpp",
+    ],
+    static_libs: [
+        "VtsHalWifiV1_0TargetTestUtil",
+        "android.hardware.wifi@1.0",
+        "android.hardware.wifi@1.1",
+        "android.hardware.wifi@1.2",
+        "android.hardware.wifi@1.3",
+        "android.hardware.wifi@1.4",
+        "android.hardware.wifi@1.5",
+        "libwifi-system-iface",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
+
 // These tests are split out so that they can be conditioned on presence of the
 // "android.hardware.wifi.aware" feature.
 cc_test {
diff --git a/wifi/1.5/vts/functional/wifi_chip_hidl_test.cpp b/wifi/1.5/vts/functional/wifi_chip_hidl_test.cpp
new file mode 100644
index 0000000..eeaa7e0
--- /dev/null
+++ b/wifi/1.5/vts/functional/wifi_chip_hidl_test.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2020 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 <VtsHalHidlTargetCallbackBase.h>
+#include <android-base/logging.h>
+
+#undef NAN  // NAN is defined in bionic/libc/include/math.h:38
+
+#include <android/hardware/wifi/1.4/IWifiChipEventCallback.h>
+#include <android/hardware/wifi/1.5/IWifi.h>
+#include <android/hardware/wifi/1.5/IWifiChip.h>
+#include <android/hardware/wifi/1.5/IWifiStaIface.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+#include "wifi_hidl_call_util.h"
+#include "wifi_hidl_test_utils.h"
+
+using ::android::sp;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::wifi::V1_0::ChipModeId;
+using ::android::hardware::wifi::V1_0::IfaceType;
+using ::android::hardware::wifi::V1_0::IWifiIface;
+using ::android::hardware::wifi::V1_0::IWifiStaIface;
+using ::android::hardware::wifi::V1_0::WifiDebugRingBufferStatus;
+using ::android::hardware::wifi::V1_0::WifiStatus;
+using ::android::hardware::wifi::V1_0::WifiStatusCode;
+using ::android::hardware::wifi::V1_4::IWifiChipEventCallback;
+using ::android::hardware::wifi::V1_5::IWifiChip;
+
+/**
+ * Fixture to use for all Wifi chip HIDL interface tests.
+ */
+class WifiChipHidlTest : public ::testing::TestWithParam<std::string> {
+   public:
+    virtual void SetUp() override {
+        // Make sure to start with a clean state
+        stopWifi(GetInstanceName());
+
+        wifi_chip_ = IWifiChip::castFrom(getWifiChip(GetInstanceName()));
+        ASSERT_NE(nullptr, wifi_chip_.get());
+    }
+
+    virtual void TearDown() override { stopWifi(GetInstanceName()); }
+
+   protected:
+    // Helper function to configure the Chip in one of the supported modes.
+    // Most of the non-mode-configuration-related methods require chip
+    // to be first configured.
+    ChipModeId configureChipForIfaceType(IfaceType type, bool expectSuccess) {
+        ChipModeId mode_id;
+        EXPECT_EQ(expectSuccess,
+                  configureChipToSupportIfaceType(wifi_chip_, type, &mode_id));
+        return mode_id;
+    }
+
+    WifiStatusCode createStaIface(sp<IWifiStaIface>* sta_iface) {
+        const auto& status_and_iface = HIDL_INVOKE(wifi_chip_, createStaIface);
+        *sta_iface = status_and_iface.second;
+        return status_and_iface.first.code;
+    }
+
+    std::string getIfaceName(const sp<IWifiIface>& iface) {
+        const auto& status_and_name = HIDL_INVOKE(iface, getName);
+        EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_name.first.code);
+        return status_and_name.second;
+    }
+
+    std::vector<sp<IWifiStaIface>> create2StaIfacesIfPossible() {
+        configureChipForIfaceType(IfaceType::STA, true);
+        sp<IWifiStaIface> iface1, iface2;
+        EXPECT_EQ(WifiStatusCode::SUCCESS, createStaIface(&iface1));
+        EXPECT_NE(nullptr, iface1.get());
+
+        // Try to create 2nd iface
+        auto status = createStaIface(&iface2);
+        if (status != WifiStatusCode::SUCCESS) {
+            return {iface1};
+        }
+        EXPECT_NE(nullptr, iface2.get());
+        return {iface1, iface2};
+    }
+
+    sp<IWifiChip> wifi_chip_;
+
+   private:
+    std::string GetInstanceName() { return GetParam(); }
+};
+
+/*
+ * setMultiStaPrimaryConnection
+ *
+ * Only run if device supports 2 STA ifaces.
+ */
+TEST_P(WifiChipHidlTest, setMultiStaPrimaryConnection) {
+    auto ifaces = create2StaIfacesIfPossible();
+    if (ifaces.size() < 2) {
+        GTEST_SKIP() << "Device does not support more than 1 STA concurrently";
+    }
+
+    const auto& status = HIDL_INVOKE(wifi_chip_, setMultiStaPrimaryConnection,
+                                     getIfaceName(ifaces.front()));
+    if (status.code != WifiStatusCode::SUCCESS) {
+        EXPECT_EQ(WifiStatusCode::ERROR_NOT_SUPPORTED, status.code);
+    }
+}
+
+/*
+ * setMultiStaUseCase
+ *
+ * Only run if device supports 2 STA ifaces.
+ */
+TEST_P(WifiChipHidlTest, setMultiStaUseCase) {
+    auto ifaces = create2StaIfacesIfPossible();
+    if (ifaces.size() < 2) {
+        GTEST_SKIP() << "Device does not support more than 1 STA concurrently";
+    }
+
+    const auto& status = HIDL_INVOKE(
+        wifi_chip_, setMultiStaUseCase,
+        IWifiChip::MultiStaUseCase::DUAL_STA_TRANSIENT_PREFER_PRIMARY);
+    if (status.code != WifiStatusCode::SUCCESS) {
+        EXPECT_EQ(WifiStatusCode::ERROR_NOT_SUPPORTED, status.code);
+    }
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WifiChipHidlTest);
+INSTANTIATE_TEST_SUITE_P(
+    PerInstance, WifiChipHidlTest,
+    testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+        ::android::hardware::wifi::V1_5::IWifi::descriptor)),
+    android::hardware::PrintInstanceNameToString);
diff --git a/wifi/1.5/vts/functional/wifi_sta_iface_hidl_test.cpp b/wifi/1.5/vts/functional/wifi_sta_iface_hidl_test.cpp
new file mode 100644
index 0000000..8cc3300
--- /dev/null
+++ b/wifi/1.5/vts/functional/wifi_sta_iface_hidl_test.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 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 <numeric>
+#include <vector>
+
+#include <android-base/logging.h>
+
+#include <android/hardware/wifi/1.5/IWifi.h>
+#include <android/hardware/wifi/1.5/IWifiChip.h>
+#include <android/hardware/wifi/1.5/IWifiStaIface.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+#include "wifi_hidl_call_util.h"
+#include "wifi_hidl_test_utils.h"
+
+using ::android::sp;
+using ::android::hardware::hidl_array;
+using ::android::hardware::wifi::V1_0::WifiStatus;
+using ::android::hardware::wifi::V1_0::WifiStatusCode;
+using ::android::hardware::wifi::V1_5::IWifiChip;
+using ::android::hardware::wifi::V1_5::IWifiStaIface;
+
+/**
+ * Fixture to use for all STA Iface HIDL interface tests.
+ */
+class WifiStaIfaceHidlTest : public ::testing::TestWithParam<std::string> {
+   public:
+    virtual void SetUp() override {
+        // Make sure to start with a clean state
+        stopWifi(GetInstanceName());
+
+        wifi_sta_iface_ =
+            IWifiStaIface::castFrom(getWifiStaIface(GetInstanceName()));
+        ASSERT_NE(nullptr, wifi_sta_iface_.get());
+    }
+
+    virtual void TearDown() override { stopWifi(GetInstanceName()); }
+
+   protected:
+    bool isCapabilitySupported(IWifiStaIface::StaIfaceCapabilityMask cap_mask) {
+        const auto& status_and_caps =
+            HIDL_INVOKE(wifi_sta_iface_, getCapabilities);
+        EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_caps.first.code);
+        return (status_and_caps.second & cap_mask) != 0;
+    }
+
+    WifiStatusCode createStaIface(sp<IWifiStaIface>* sta_iface) {
+        sp<IWifiChip> wifi_chip =
+            IWifiChip::castFrom(getWifiChip(GetInstanceName()));
+        EXPECT_NE(nullptr, wifi_chip.get());
+        const auto& status_and_iface = HIDL_INVOKE(wifi_chip, createStaIface);
+        *sta_iface = IWifiStaIface::castFrom(status_and_iface.second);
+        return status_and_iface.first.code;
+    }
+
+    sp<IWifiStaIface> wifi_sta_iface_;
+
+   private:
+    std::string GetInstanceName() { return GetParam(); }
+};
+
+/*
+ * GetLinkLayerStats_1_5
+ * Ensures that calls to get link layer stats V1_5 will retrieve a non-empty
+ * StaLinkLayerStats after link layer stats collection is enabled.
+ */
+TEST_P(WifiStaIfaceHidlTest, GetLinkLayerStats_1_5) {
+    if (!isCapabilitySupported(
+            IWifiStaIface::StaIfaceCapabilityMask::LINK_LAYER_STATS)) {
+        // No-op if link layer stats is not supported.
+        return;
+    }
+
+    // Enable link layer stats collection.
+    EXPECT_EQ(WifiStatusCode::SUCCESS,
+              HIDL_INVOKE(wifi_sta_iface_, enableLinkLayerStatsCollection, true)
+                  .code);
+    // Retrieve link layer stats.
+    const auto& status_and_stats =
+        HIDL_INVOKE(wifi_sta_iface_, getLinkLayerStats_1_5);
+    EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_stats.first.code);
+    EXPECT_GT(status_and_stats.second.timeStampInMs, 0u);
+    // Try to create 2nd iface. If yes, it should fill in the duty cycle field.
+    sp<IWifiStaIface> iface;
+    auto status = createStaIface(&iface);
+    if (status == WifiStatusCode::SUCCESS) {
+        EXPECT_GT(status_and_stats.second.iface.timeSliceDutyCycleInPercent,
+                  0u);
+    }
+    // Disable link layer stats collection.
+    EXPECT_EQ(
+        WifiStatusCode::SUCCESS,
+        HIDL_INVOKE(wifi_sta_iface_, disableLinkLayerStatsCollection).code);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WifiStaIfaceHidlTest);
+INSTANTIATE_TEST_SUITE_P(
+    PerInstance, WifiStaIfaceHidlTest,
+    testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+        ::android::hardware::wifi::V1_5::IWifi::descriptor)),
+    android::hardware::PrintInstanceNameToString);