Merge changes from topic "Wi-Fi7: MLO Stats"

* changes:
  wifi: Add Support for Multi-Link Layer stats in shim layer
  wifi: Add multi link layer stats support to AIDL
  wifi: Update vts test for GetLinkLayerStats()
  wifi: Add unit test for multi link stats conversion
  wifi: Fix unit test for LegacyLinkLayerStats
diff --git a/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/StaLinkLayerIfaceStats.aidl b/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/StaLinkLayerIfaceStats.aidl
index cf68fc6..a419207 100644
--- a/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/StaLinkLayerIfaceStats.aidl
+++ b/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/StaLinkLayerIfaceStats.aidl
@@ -34,16 +34,5 @@
 package android.hardware.wifi;
 @VintfStability
 parcelable StaLinkLayerIfaceStats {
-  int beaconRx;
-  int avgRssiMgmt;
-  android.hardware.wifi.StaLinkLayerIfacePacketStats wmeBePktStats;
-  android.hardware.wifi.StaLinkLayerIfacePacketStats wmeBkPktStats;
-  android.hardware.wifi.StaLinkLayerIfacePacketStats wmeViPktStats;
-  android.hardware.wifi.StaLinkLayerIfacePacketStats wmeVoPktStats;
-  byte timeSliceDutyCycleInPercent;
-  android.hardware.wifi.StaLinkLayerIfaceContentionTimeStats wmeBeContentionTimeStats;
-  android.hardware.wifi.StaLinkLayerIfaceContentionTimeStats wmeBkContentionTimeStats;
-  android.hardware.wifi.StaLinkLayerIfaceContentionTimeStats wmeViContentionTimeStats;
-  android.hardware.wifi.StaLinkLayerIfaceContentionTimeStats wmeVoContentionTimeStats;
-  android.hardware.wifi.StaPeerInfo[] peers;
+  android.hardware.wifi.StaLinkLayerLinkStats[] links;
 }
diff --git a/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/StaLinkLayerLinkStats.aidl b/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/StaLinkLayerLinkStats.aidl
new file mode 100644
index 0000000..714bba8
--- /dev/null
+++ b/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/StaLinkLayerLinkStats.aidl
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.wifi;
+@VintfStability
+parcelable StaLinkLayerLinkStats {
+  int linkId;
+  int beaconRx;
+  int avgRssiMgmt;
+  android.hardware.wifi.StaLinkLayerIfacePacketStats wmeBePktStats;
+  android.hardware.wifi.StaLinkLayerIfacePacketStats wmeBkPktStats;
+  android.hardware.wifi.StaLinkLayerIfacePacketStats wmeViPktStats;
+  android.hardware.wifi.StaLinkLayerIfacePacketStats wmeVoPktStats;
+  byte timeSliceDutyCycleInPercent;
+  android.hardware.wifi.StaLinkLayerIfaceContentionTimeStats wmeBeContentionTimeStats;
+  android.hardware.wifi.StaLinkLayerIfaceContentionTimeStats wmeBkContentionTimeStats;
+  android.hardware.wifi.StaLinkLayerIfaceContentionTimeStats wmeViContentionTimeStats;
+  android.hardware.wifi.StaLinkLayerIfaceContentionTimeStats wmeVoContentionTimeStats;
+  android.hardware.wifi.StaPeerInfo[] peers;
+}
diff --git a/wifi/aidl/android/hardware/wifi/StaLinkLayerIfaceStats.aidl b/wifi/aidl/android/hardware/wifi/StaLinkLayerIfaceStats.aidl
index 78f8caa..3f8718f 100644
--- a/wifi/aidl/android/hardware/wifi/StaLinkLayerIfaceStats.aidl
+++ b/wifi/aidl/android/hardware/wifi/StaLinkLayerIfaceStats.aidl
@@ -16,64 +16,13 @@
 
 package android.hardware.wifi;
 
-import android.hardware.wifi.StaLinkLayerIfaceContentionTimeStats;
-import android.hardware.wifi.StaLinkLayerIfacePacketStats;
-import android.hardware.wifi.StaPeerInfo;
+import android.hardware.wifi.StaLinkLayerLinkStats;
 
 /**
- * Iface statistics for the current connection.
+ * Iface statistics for the current connection. Current connection may have
+ * single or multiple links.
  */
 @VintfStability
 parcelable StaLinkLayerIfaceStats {
-    /**
-     * Number beacons received from the connected AP.
-     */
-    int beaconRx;
-    /**
-     * Access Point Beacon and Management frames RSSI (averaged).
-     */
-    int avgRssiMgmt;
-    /**
-     * WME Best Effort Access Category packet counters.
-     */
-    StaLinkLayerIfacePacketStats wmeBePktStats;
-    /**
-     * WME Background Access Category packet counters.
-     */
-    StaLinkLayerIfacePacketStats wmeBkPktStats;
-    /**
-     * WME Video Access Category packet counters.
-     */
-    StaLinkLayerIfacePacketStats wmeViPktStats;
-    /**
-     * WME Voice Access Category packet counters.
-     */
-    StaLinkLayerIfacePacketStats wmeVoPktStats;
-    /**
-     * 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.
-     */
-    byte timeSliceDutyCycleInPercent;
-    /**
-     * WME Best Effort (BE) Access Category (AC) contention time statistics.
-     */
-    StaLinkLayerIfaceContentionTimeStats wmeBeContentionTimeStats;
-    /**
-     * WME Background (BK) Access Category (AC) contention time statistics.
-     */
-    StaLinkLayerIfaceContentionTimeStats wmeBkContentionTimeStats;
-    /**
-     * WME Video (VI) Access Category (AC) contention time statistics.
-     */
-    StaLinkLayerIfaceContentionTimeStats wmeViContentionTimeStats;
-    /**
-     * WME Voice (VO) Access Category (AC) contention time statistics.
-     */
-    StaLinkLayerIfaceContentionTimeStats wmeVoContentionTimeStats;
-    /**
-     * Per peer statistics.
-     */
-    StaPeerInfo[] peers;
+    StaLinkLayerLinkStats[] links;
 }
diff --git a/wifi/aidl/android/hardware/wifi/StaLinkLayerLinkStats.aidl b/wifi/aidl/android/hardware/wifi/StaLinkLayerLinkStats.aidl
new file mode 100644
index 0000000..cf1a867
--- /dev/null
+++ b/wifi/aidl/android/hardware/wifi/StaLinkLayerLinkStats.aidl
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 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;
+
+import android.hardware.wifi.StaLinkLayerIfaceContentionTimeStats;
+import android.hardware.wifi.StaLinkLayerIfacePacketStats;
+import android.hardware.wifi.StaPeerInfo;
+
+/**
+ * Per Link statistics for the current connection. For MLO, this is
+ * the statistics for one link in the connection.
+ */
+@VintfStability
+parcelable StaLinkLayerLinkStats {
+    /**
+     * Identifier for the link within MLO. For single link operation this field
+     * is not relevant and can be set to 0.
+     */
+    int linkId;
+    /**
+     * Number of beacons received from the connected AP on the link.
+     */
+    int beaconRx;
+    /**
+     * Access Point Beacon and Management frames RSSI (averaged) on the link.
+     */
+    int avgRssiMgmt;
+    /**
+     * WME Best Effort Access Category packet counters on the link.
+     */
+    StaLinkLayerIfacePacketStats wmeBePktStats;
+    /**
+     * WME Background Access Category packet counters on the link.
+     */
+    StaLinkLayerIfacePacketStats wmeBkPktStats;
+    /**
+     * WME Video Access Category packet counters on the link.
+     */
+    StaLinkLayerIfacePacketStats wmeViPktStats;
+    /**
+     * WME Voice Access Category packet counters on the link.
+     */
+    StaLinkLayerIfacePacketStats wmeVoPktStats;
+    /**
+     * Duty cycle for the link.
+     * If this link is being served using time slicing on a radio with one or
+     * more links then the duty cycle assigned to this link in %. If not using
+     * time slicing, set to 100.
+     */
+    byte timeSliceDutyCycleInPercent;
+    /**
+     * WME Best Effort (BE) Access Category (AC) contention time statistics on
+     * the link.
+     */
+    StaLinkLayerIfaceContentionTimeStats wmeBeContentionTimeStats;
+    /**
+     * WME Background (BK) Access Category (AC) contention time statistics on
+     * the link.
+     */
+    StaLinkLayerIfaceContentionTimeStats wmeBkContentionTimeStats;
+    /**
+     * WME Video (VI) Access Category (AC) contention time statistics on the
+     * link.
+     */
+    StaLinkLayerIfaceContentionTimeStats wmeViContentionTimeStats;
+    /**
+     * WME Voice (VO) Access Category (AC) contention time statistics on the
+     * link.
+     */
+    StaLinkLayerIfaceContentionTimeStats wmeVoContentionTimeStats;
+    /**
+     * Per peer statistics for the link.
+     */
+    StaPeerInfo[] peers;
+}
diff --git a/wifi/aidl/default/aidl_struct_util.cpp b/wifi/aidl/default/aidl_struct_util.cpp
index ec8b396..fe7579b 100644
--- a/wifi/aidl/default/aidl_struct_util.cpp
+++ b/wifi/aidl/default/aidl_struct_util.cpp
@@ -940,69 +940,158 @@
     return true;
 }
 
+bool convertLegacyLinkLayerMlStatsToAidl(const legacy_hal::LinkLayerMlStats& legacy_ml_stats,
+                                         StaLinkLayerStats* aidl_stats) {
+    if (!aidl_stats) {
+        return false;
+    }
+    *aidl_stats = {};
+    std::vector<StaLinkLayerLinkStats> links;
+    // Iterate over each links
+    for (const auto& link : legacy_ml_stats.links) {
+        StaLinkLayerLinkStats linkStats = {};
+        linkStats.linkId = link.stat.link_id;
+        linkStats.beaconRx = link.stat.beacon_rx;
+        linkStats.avgRssiMgmt = link.stat.rssi_mgmt;
+        linkStats.wmeBePktStats.rxMpdu = link.stat.ac[legacy_hal::WIFI_AC_BE].rx_mpdu;
+        linkStats.wmeBePktStats.txMpdu = link.stat.ac[legacy_hal::WIFI_AC_BE].tx_mpdu;
+        linkStats.wmeBePktStats.lostMpdu = link.stat.ac[legacy_hal::WIFI_AC_BE].mpdu_lost;
+        linkStats.wmeBePktStats.retries = link.stat.ac[legacy_hal::WIFI_AC_BE].retries;
+        linkStats.wmeBeContentionTimeStats.contentionTimeMinInUsec =
+                link.stat.ac[legacy_hal::WIFI_AC_BE].contention_time_min;
+        linkStats.wmeBeContentionTimeStats.contentionTimeMaxInUsec =
+                link.stat.ac[legacy_hal::WIFI_AC_BE].contention_time_max;
+        linkStats.wmeBeContentionTimeStats.contentionTimeAvgInUsec =
+                link.stat.ac[legacy_hal::WIFI_AC_BE].contention_time_avg;
+        linkStats.wmeBeContentionTimeStats.contentionNumSamples =
+                link.stat.ac[legacy_hal::WIFI_AC_BE].contention_num_samples;
+        linkStats.wmeBkPktStats.rxMpdu = link.stat.ac[legacy_hal::WIFI_AC_BK].rx_mpdu;
+        linkStats.wmeBkPktStats.txMpdu = link.stat.ac[legacy_hal::WIFI_AC_BK].tx_mpdu;
+        linkStats.wmeBkPktStats.lostMpdu = link.stat.ac[legacy_hal::WIFI_AC_BK].mpdu_lost;
+        linkStats.wmeBkPktStats.retries = link.stat.ac[legacy_hal::WIFI_AC_BK].retries;
+        linkStats.wmeBkContentionTimeStats.contentionTimeMinInUsec =
+                link.stat.ac[legacy_hal::WIFI_AC_BK].contention_time_min;
+        linkStats.wmeBkContentionTimeStats.contentionTimeMaxInUsec =
+                link.stat.ac[legacy_hal::WIFI_AC_BK].contention_time_max;
+        linkStats.wmeBkContentionTimeStats.contentionTimeAvgInUsec =
+                link.stat.ac[legacy_hal::WIFI_AC_BK].contention_time_avg;
+        linkStats.wmeBkContentionTimeStats.contentionNumSamples =
+                link.stat.ac[legacy_hal::WIFI_AC_BK].contention_num_samples;
+        linkStats.wmeViPktStats.rxMpdu = link.stat.ac[legacy_hal::WIFI_AC_VI].rx_mpdu;
+        linkStats.wmeViPktStats.txMpdu = link.stat.ac[legacy_hal::WIFI_AC_VI].tx_mpdu;
+        linkStats.wmeViPktStats.lostMpdu = link.stat.ac[legacy_hal::WIFI_AC_VI].mpdu_lost;
+        linkStats.wmeViPktStats.retries = link.stat.ac[legacy_hal::WIFI_AC_VI].retries;
+        linkStats.wmeViContentionTimeStats.contentionTimeMinInUsec =
+                link.stat.ac[legacy_hal::WIFI_AC_VI].contention_time_min;
+        linkStats.wmeViContentionTimeStats.contentionTimeMaxInUsec =
+                link.stat.ac[legacy_hal::WIFI_AC_VI].contention_time_max;
+        linkStats.wmeViContentionTimeStats.contentionTimeAvgInUsec =
+                link.stat.ac[legacy_hal::WIFI_AC_VI].contention_time_avg;
+        linkStats.wmeViContentionTimeStats.contentionNumSamples =
+                link.stat.ac[legacy_hal::WIFI_AC_VI].contention_num_samples;
+        linkStats.wmeVoPktStats.rxMpdu = link.stat.ac[legacy_hal::WIFI_AC_VO].rx_mpdu;
+        linkStats.wmeVoPktStats.txMpdu = link.stat.ac[legacy_hal::WIFI_AC_VO].tx_mpdu;
+        linkStats.wmeVoPktStats.lostMpdu = link.stat.ac[legacy_hal::WIFI_AC_VO].mpdu_lost;
+        linkStats.wmeVoPktStats.retries = link.stat.ac[legacy_hal::WIFI_AC_VO].retries;
+        linkStats.wmeVoContentionTimeStats.contentionTimeMinInUsec =
+                link.stat.ac[legacy_hal::WIFI_AC_VO].contention_time_min;
+        linkStats.wmeVoContentionTimeStats.contentionTimeMaxInUsec =
+                link.stat.ac[legacy_hal::WIFI_AC_VO].contention_time_max;
+        linkStats.wmeVoContentionTimeStats.contentionTimeAvgInUsec =
+                link.stat.ac[legacy_hal::WIFI_AC_VO].contention_time_avg;
+        linkStats.wmeVoContentionTimeStats.contentionNumSamples =
+                link.stat.ac[legacy_hal::WIFI_AC_VO].contention_num_samples;
+        linkStats.timeSliceDutyCycleInPercent = link.stat.time_slicing_duty_cycle_percent;
+        // peer info legacy_stats conversion.
+        std::vector<StaPeerInfo> aidl_peers_info_stats;
+        for (const auto& legacy_peer_info_stats : link.peers) {
+            StaPeerInfo aidl_peer_info_stats;
+            if (!convertLegacyPeerInfoStatsToAidl(legacy_peer_info_stats, &aidl_peer_info_stats)) {
+                return false;
+            }
+            aidl_peers_info_stats.push_back(aidl_peer_info_stats);
+        }
+        linkStats.peers = aidl_peers_info_stats;
+        // Push link stats to aidl stats.
+        links.push_back(linkStats);
+    }
+    aidl_stats->iface.links = links;
+    // radio legacy_stats conversion.
+    std::vector<StaLinkLayerRadioStats> aidl_radios_stats;
+    for (const auto& legacy_radio_stats : legacy_ml_stats.radios) {
+        StaLinkLayerRadioStats aidl_radio_stats;
+        if (!convertLegacyLinkLayerRadioStatsToAidl(legacy_radio_stats, &aidl_radio_stats)) {
+            return false;
+        }
+        aidl_radios_stats.push_back(aidl_radio_stats);
+    }
+    aidl_stats->radios = aidl_radios_stats;
+    aidl_stats->timeStampInMs = ::android::uptimeMillis();
+
+    return true;
+}
+
 bool convertLegacyLinkLayerStatsToAidl(const legacy_hal::LinkLayerStats& legacy_stats,
                                        StaLinkLayerStats* aidl_stats) {
     if (!aidl_stats) {
         return false;
     }
     *aidl_stats = {};
+    std::vector<StaLinkLayerLinkStats> links;
+    StaLinkLayerLinkStats linkStats = {};
     // iface legacy_stats conversion.
-    aidl_stats->iface.beaconRx = legacy_stats.iface.beacon_rx;
-    aidl_stats->iface.avgRssiMgmt = legacy_stats.iface.rssi_mgmt;
-    aidl_stats->iface.wmeBePktStats.rxMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].rx_mpdu;
-    aidl_stats->iface.wmeBePktStats.txMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].tx_mpdu;
-    aidl_stats->iface.wmeBePktStats.lostMpdu =
-            legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].mpdu_lost;
-    aidl_stats->iface.wmeBePktStats.retries = legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].retries;
-    aidl_stats->iface.wmeBeContentionTimeStats.contentionTimeMinInUsec =
+    linkStats.linkId = 0;
+    linkStats.beaconRx = legacy_stats.iface.beacon_rx;
+    linkStats.avgRssiMgmt = legacy_stats.iface.rssi_mgmt;
+    linkStats.wmeBePktStats.rxMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].rx_mpdu;
+    linkStats.wmeBePktStats.txMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].tx_mpdu;
+    linkStats.wmeBePktStats.lostMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].mpdu_lost;
+    linkStats.wmeBePktStats.retries = legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].retries;
+    linkStats.wmeBeContentionTimeStats.contentionTimeMinInUsec =
             legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].contention_time_min;
-    aidl_stats->iface.wmeBeContentionTimeStats.contentionTimeMaxInUsec =
+    linkStats.wmeBeContentionTimeStats.contentionTimeMaxInUsec =
             legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].contention_time_max;
-    aidl_stats->iface.wmeBeContentionTimeStats.contentionTimeAvgInUsec =
+    linkStats.wmeBeContentionTimeStats.contentionTimeAvgInUsec =
             legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].contention_time_avg;
-    aidl_stats->iface.wmeBeContentionTimeStats.contentionNumSamples =
+    linkStats.wmeBeContentionTimeStats.contentionNumSamples =
             legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].contention_num_samples;
-    aidl_stats->iface.wmeBkPktStats.rxMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].rx_mpdu;
-    aidl_stats->iface.wmeBkPktStats.txMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].tx_mpdu;
-    aidl_stats->iface.wmeBkPktStats.lostMpdu =
-            legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].mpdu_lost;
-    aidl_stats->iface.wmeBkPktStats.retries = legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].retries;
-    aidl_stats->iface.wmeBkContentionTimeStats.contentionTimeMinInUsec =
+    linkStats.wmeBkPktStats.rxMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].rx_mpdu;
+    linkStats.wmeBkPktStats.txMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].tx_mpdu;
+    linkStats.wmeBkPktStats.lostMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].mpdu_lost;
+    linkStats.wmeBkPktStats.retries = legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].retries;
+    linkStats.wmeBkContentionTimeStats.contentionTimeMinInUsec =
             legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].contention_time_min;
-    aidl_stats->iface.wmeBkContentionTimeStats.contentionTimeMaxInUsec =
+    linkStats.wmeBkContentionTimeStats.contentionTimeMaxInUsec =
             legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].contention_time_max;
-    aidl_stats->iface.wmeBkContentionTimeStats.contentionTimeAvgInUsec =
+    linkStats.wmeBkContentionTimeStats.contentionTimeAvgInUsec =
             legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].contention_time_avg;
-    aidl_stats->iface.wmeBkContentionTimeStats.contentionNumSamples =
+    linkStats.wmeBkContentionTimeStats.contentionNumSamples =
             legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].contention_num_samples;
-    aidl_stats->iface.wmeViPktStats.rxMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].rx_mpdu;
-    aidl_stats->iface.wmeViPktStats.txMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].tx_mpdu;
-    aidl_stats->iface.wmeViPktStats.lostMpdu =
-            legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].mpdu_lost;
-    aidl_stats->iface.wmeViPktStats.retries = legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].retries;
-    aidl_stats->iface.wmeViContentionTimeStats.contentionTimeMinInUsec =
+    linkStats.wmeViPktStats.rxMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].rx_mpdu;
+    linkStats.wmeViPktStats.txMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].tx_mpdu;
+    linkStats.wmeViPktStats.lostMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].mpdu_lost;
+    linkStats.wmeViPktStats.retries = legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].retries;
+    linkStats.wmeViContentionTimeStats.contentionTimeMinInUsec =
             legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].contention_time_min;
-    aidl_stats->iface.wmeViContentionTimeStats.contentionTimeMaxInUsec =
+    linkStats.wmeViContentionTimeStats.contentionTimeMaxInUsec =
             legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].contention_time_max;
-    aidl_stats->iface.wmeViContentionTimeStats.contentionTimeAvgInUsec =
+    linkStats.wmeViContentionTimeStats.contentionTimeAvgInUsec =
             legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].contention_time_avg;
-    aidl_stats->iface.wmeViContentionTimeStats.contentionNumSamples =
+    linkStats.wmeViContentionTimeStats.contentionNumSamples =
             legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].contention_num_samples;
-    aidl_stats->iface.wmeVoPktStats.rxMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].rx_mpdu;
-    aidl_stats->iface.wmeVoPktStats.txMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].tx_mpdu;
-    aidl_stats->iface.wmeVoPktStats.lostMpdu =
-            legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].mpdu_lost;
-    aidl_stats->iface.wmeVoPktStats.retries = legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].retries;
-    aidl_stats->iface.wmeVoContentionTimeStats.contentionTimeMinInUsec =
+    linkStats.wmeVoPktStats.rxMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].rx_mpdu;
+    linkStats.wmeVoPktStats.txMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].tx_mpdu;
+    linkStats.wmeVoPktStats.lostMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].mpdu_lost;
+    linkStats.wmeVoPktStats.retries = legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].retries;
+    linkStats.wmeVoContentionTimeStats.contentionTimeMinInUsec =
             legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].contention_time_min;
-    aidl_stats->iface.wmeVoContentionTimeStats.contentionTimeMaxInUsec =
+    linkStats.wmeVoContentionTimeStats.contentionTimeMaxInUsec =
             legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].contention_time_max;
-    aidl_stats->iface.wmeVoContentionTimeStats.contentionTimeAvgInUsec =
+    linkStats.wmeVoContentionTimeStats.contentionTimeAvgInUsec =
             legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].contention_time_avg;
-    aidl_stats->iface.wmeVoContentionTimeStats.contentionNumSamples =
+    linkStats.wmeVoContentionTimeStats.contentionNumSamples =
             legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].contention_num_samples;
-    aidl_stats->iface.timeSliceDutyCycleInPercent =
-            legacy_stats.iface.info.time_slicing_duty_cycle_percent;
+    linkStats.timeSliceDutyCycleInPercent = legacy_stats.iface.info.time_slicing_duty_cycle_percent;
     // peer info legacy_stats conversion.
     std::vector<StaPeerInfo> aidl_peers_info_stats;
     for (const auto& legacy_peer_info_stats : legacy_stats.peers) {
@@ -1012,7 +1101,9 @@
         }
         aidl_peers_info_stats.push_back(aidl_peer_info_stats);
     }
-    aidl_stats->iface.peers = aidl_peers_info_stats;
+    linkStats.peers = aidl_peers_info_stats;
+    links.push_back(linkStats);
+    aidl_stats->iface.links = links;
     // radio legacy_stats conversion.
     std::vector<StaLinkLayerRadioStats> aidl_radios_stats;
     for (const auto& legacy_radio_stats : legacy_stats.radios) {
diff --git a/wifi/aidl/default/aidl_struct_util.h b/wifi/aidl/default/aidl_struct_util.h
index d8e1fd4..ac86755 100644
--- a/wifi/aidl/default/aidl_struct_util.h
+++ b/wifi/aidl/default/aidl_struct_util.h
@@ -89,6 +89,8 @@
 bool convertLegacyVectorOfCachedGscanResultsToAidl(
         const std::vector<legacy_hal::wifi_cached_scan_results>& legacy_cached_scan_results,
         std::vector<StaScanData>* aidl_scan_datas);
+bool convertLegacyLinkLayerMlStatsToAidl(const legacy_hal::LinkLayerMlStats& legacy_ml_stats,
+                                         StaLinkLayerStats* aidl_stats);
 bool convertLegacyLinkLayerStatsToAidl(const legacy_hal::LinkLayerStats& legacy_stats,
                                        StaLinkLayerStats* aidl_stats);
 bool convertLegacyRoamingCapabilitiesToAidl(
diff --git a/wifi/aidl/default/tests/aidl_struct_util_unit_tests.cpp b/wifi/aidl/default/tests/aidl_struct_util_unit_tests.cpp
index 4a69c24..c2de012 100644
--- a/wifi/aidl/default/tests/aidl_struct_util_unit_tests.cpp
+++ b/wifi/aidl/default/tests/aidl_struct_util_unit_tests.cpp
@@ -112,6 +112,285 @@
     EXPECT_EQ(static_cast<int32_t>(legacy_iface_info2.channel), aidl_iface_info2.channel);
 }
 
+TEST_F(AidlStructUtilTest, canConvertLegacyLinkLayerMlStatsToAidl) {
+    legacy_hal::LinkLayerMlStats legacy_ml_stats{};
+    // Add two radio stats
+    legacy_ml_stats.radios.push_back(legacy_hal::LinkLayerRadioStats{});
+    legacy_ml_stats.radios.push_back(legacy_hal::LinkLayerRadioStats{});
+    // Add two links.
+    legacy_ml_stats.links.push_back(legacy_hal::LinkStats{});
+    legacy_ml_stats.links.push_back(legacy_hal::LinkStats{});
+    // Set stats for each link.
+    for (legacy_hal::LinkStats& link : legacy_ml_stats.links) {
+        link.peers.push_back(legacy_hal::WifiPeerInfo{});
+        link.peers.push_back(legacy_hal::WifiPeerInfo{});
+        link.stat.beacon_rx = rand();
+        link.stat.rssi_mgmt = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_BE].rx_mpdu = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_BE].tx_mpdu = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_BE].mpdu_lost = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_BE].retries = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_BE].contention_time_min = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_BE].contention_time_max = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_BE].contention_time_avg = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_BE].contention_num_samples = rand();
+
+        link.stat.ac[legacy_hal::WIFI_AC_BK].rx_mpdu = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_BK].tx_mpdu = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_BK].mpdu_lost = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_BK].retries = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_BK].contention_time_min = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_BK].contention_time_max = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_BK].contention_time_avg = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_BK].contention_num_samples = rand();
+
+        link.stat.ac[legacy_hal::WIFI_AC_VI].rx_mpdu = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_VI].tx_mpdu = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_VI].mpdu_lost = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_VI].retries = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_VI].contention_time_min = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_VI].contention_time_max = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_VI].contention_time_avg = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_VI].contention_num_samples = rand();
+
+        link.stat.ac[legacy_hal::WIFI_AC_VO].rx_mpdu = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_VO].tx_mpdu = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_VO].mpdu_lost = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_VO].retries = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_VO].contention_time_min = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_VO].contention_time_max = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_VO].contention_time_avg = rand();
+        link.stat.ac[legacy_hal::WIFI_AC_VO].contention_num_samples = rand();
+
+        link.stat.time_slicing_duty_cycle_percent = rand();
+        link.stat.num_peers = 2;
+
+        // Set peer stats for each of the peers.
+        for (auto& peer : link.peers) {
+            peer.peer_info.bssload.sta_count = rand();
+            peer.peer_info.bssload.chan_util = rand();
+            wifi_rate_stat rate_stat1 = {
+                    .rate = {3, 1, 2, 5, 0, 0},
+                    .tx_mpdu = 0,
+                    .rx_mpdu = 1,
+                    .mpdu_lost = 2,
+                    .retries = 3,
+                    .retries_short = 4,
+                    .retries_long = 5,
+            };
+            wifi_rate_stat rate_stat2 = {
+                    .rate = {2, 2, 1, 6, 0, 1},
+                    .tx_mpdu = 6,
+                    .rx_mpdu = 7,
+                    .mpdu_lost = 8,
+                    .retries = 9,
+                    .retries_short = 10,
+                    .retries_long = 11,
+            };
+            peer.rate_stats.push_back(rate_stat1);
+            peer.rate_stats.push_back(rate_stat2);
+        }
+    }
+    // Set radio stats
+    for (auto& radio : legacy_ml_stats.radios) {
+        radio.stats.radio = rand();
+        radio.stats.on_time = rand();
+        radio.stats.tx_time = rand();
+        radio.stats.rx_time = rand();
+        radio.stats.on_time_scan = rand();
+        radio.stats.on_time_nbd = rand();
+        radio.stats.on_time_gscan = rand();
+        radio.stats.on_time_roam_scan = rand();
+        radio.stats.on_time_pno_scan = rand();
+        radio.stats.on_time_hs20 = rand();
+        for (int i = 0; i < 4; i++) {
+            radio.tx_time_per_levels.push_back(rand());
+        }
+
+        legacy_hal::wifi_channel_stat channel_stat1 = {
+                .channel = {legacy_hal::WIFI_CHAN_WIDTH_20, 2437, 2437, 0},
+                .on_time = 0x1111,
+                .cca_busy_time = 0x55,
+        };
+        legacy_hal::wifi_channel_stat channel_stat2 = {
+                .channel = {legacy_hal::WIFI_CHAN_WIDTH_20, 5180, 5180, 0},
+                .on_time = 0x2222,
+                .cca_busy_time = 0x66,
+        };
+        radio.channel_stats.push_back(channel_stat1);
+        radio.channel_stats.push_back(channel_stat2);
+    }
+    // Convert to AIDL
+    StaLinkLayerStats converted{};
+    aidl_struct_util::convertLegacyLinkLayerMlStatsToAidl(legacy_ml_stats, &converted);
+    // Validate
+    int l = 0;
+    for (legacy_hal::LinkStats& link : legacy_ml_stats.links) {
+        EXPECT_EQ(link.stat.link_id, (uint8_t)converted.iface.links[l].linkId);
+        EXPECT_EQ(link.stat.beacon_rx, (uint32_t)converted.iface.links[l].beaconRx);
+        EXPECT_EQ(link.stat.rssi_mgmt, converted.iface.links[l].avgRssiMgmt);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_BE].rx_mpdu,
+                  converted.iface.links[l].wmeBePktStats.rxMpdu);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_BE].tx_mpdu,
+                  converted.iface.links[l].wmeBePktStats.txMpdu);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_BE].mpdu_lost,
+                  converted.iface.links[l].wmeBePktStats.lostMpdu);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_BE].retries,
+                  converted.iface.links[l].wmeBePktStats.retries);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_BE].contention_time_min,
+                  (uint32_t)converted.iface.links[l]
+                          .wmeBeContentionTimeStats.contentionTimeMinInUsec);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_BE].contention_time_max,
+                  (uint32_t)converted.iface.links[l]
+                          .wmeBeContentionTimeStats.contentionTimeMaxInUsec);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_BE].contention_time_avg,
+                  (uint32_t)converted.iface.links[l]
+                          .wmeBeContentionTimeStats.contentionTimeAvgInUsec);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_BE].contention_num_samples,
+                  (uint32_t)converted.iface.links[l].wmeBeContentionTimeStats.contentionNumSamples);
+
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_BK].rx_mpdu,
+                  converted.iface.links[l].wmeBkPktStats.rxMpdu);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_BK].tx_mpdu,
+                  converted.iface.links[l].wmeBkPktStats.txMpdu);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_BK].mpdu_lost,
+                  converted.iface.links[l].wmeBkPktStats.lostMpdu);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_BK].retries,
+                  converted.iface.links[l].wmeBkPktStats.retries);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_BK].contention_time_min,
+                  (uint32_t)converted.iface.links[l]
+                          .wmeBkContentionTimeStats.contentionTimeMinInUsec);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_BK].contention_time_max,
+                  (uint32_t)converted.iface.links[l]
+                          .wmeBkContentionTimeStats.contentionTimeMaxInUsec);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_BK].contention_time_avg,
+                  (uint32_t)converted.iface.links[l]
+                          .wmeBkContentionTimeStats.contentionTimeAvgInUsec);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_BK].contention_num_samples,
+                  (uint32_t)converted.iface.links[l].wmeBkContentionTimeStats.contentionNumSamples);
+
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_VI].rx_mpdu,
+                  converted.iface.links[l].wmeViPktStats.rxMpdu);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_VI].tx_mpdu,
+                  converted.iface.links[l].wmeViPktStats.txMpdu);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_VI].mpdu_lost,
+                  converted.iface.links[l].wmeViPktStats.lostMpdu);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_VI].retries,
+                  converted.iface.links[l].wmeViPktStats.retries);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_VI].contention_time_min,
+                  (uint32_t)converted.iface.links[l]
+                          .wmeViContentionTimeStats.contentionTimeMinInUsec);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_VI].contention_time_max,
+                  (uint32_t)converted.iface.links[l]
+                          .wmeViContentionTimeStats.contentionTimeMaxInUsec);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_VI].contention_time_avg,
+                  (uint32_t)converted.iface.links[l]
+                          .wmeViContentionTimeStats.contentionTimeAvgInUsec);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_VI].contention_num_samples,
+                  (uint32_t)converted.iface.links[l].wmeViContentionTimeStats.contentionNumSamples);
+
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_VO].rx_mpdu,
+                  converted.iface.links[l].wmeVoPktStats.rxMpdu);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_VO].tx_mpdu,
+                  converted.iface.links[l].wmeVoPktStats.txMpdu);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_VO].mpdu_lost,
+                  converted.iface.links[l].wmeVoPktStats.lostMpdu);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_VO].retries,
+                  converted.iface.links[l].wmeVoPktStats.retries);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_VO].contention_time_min,
+                  (uint32_t)converted.iface.links[l]
+                          .wmeVoContentionTimeStats.contentionTimeMinInUsec);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_VO].contention_time_max,
+                  (uint32_t)converted.iface.links[l]
+                          .wmeVoContentionTimeStats.contentionTimeMaxInUsec);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_VO].contention_time_avg,
+                  (uint32_t)converted.iface.links[l]
+                          .wmeVoContentionTimeStats.contentionTimeAvgInUsec);
+        EXPECT_EQ(link.stat.ac[legacy_hal::WIFI_AC_VO].contention_num_samples,
+                  (uint32_t)converted.iface.links[l].wmeVoContentionTimeStats.contentionNumSamples);
+
+        EXPECT_EQ(link.stat.time_slicing_duty_cycle_percent,
+                  converted.iface.links[l].timeSliceDutyCycleInPercent);
+
+        EXPECT_EQ(link.peers.size(), converted.iface.links[l].peers.size());
+        for (size_t i = 0; i < link.peers.size(); i++) {
+            EXPECT_EQ(link.peers[i].peer_info.bssload.sta_count,
+                      converted.iface.links[l].peers[i].staCount);
+            EXPECT_EQ(link.peers[i].peer_info.bssload.chan_util,
+                      converted.iface.links[l].peers[i].chanUtil);
+            for (size_t j = 0; j < link.peers[i].rate_stats.size(); j++) {
+                EXPECT_EQ(
+                        link.peers[i].rate_stats[j].rate.preamble,
+                        (uint32_t)converted.iface.links[l].peers[i].rateStats[j].rateInfo.preamble);
+                EXPECT_EQ(link.peers[i].rate_stats[j].rate.nss,
+                          (uint32_t)converted.iface.links[l].peers[i].rateStats[j].rateInfo.nss);
+                EXPECT_EQ(link.peers[i].rate_stats[j].rate.bw,
+                          (uint32_t)converted.iface.links[l].peers[i].rateStats[j].rateInfo.bw);
+                EXPECT_EQ(link.peers[i].rate_stats[j].rate.rateMcsIdx,
+                          (uint32_t)converted.iface.links[l]
+                                  .peers[i]
+                                  .rateStats[j]
+                                  .rateInfo.rateMcsIdx);
+                EXPECT_EQ(link.peers[i].rate_stats[j].tx_mpdu,
+                          (uint32_t)converted.iface.links[l].peers[i].rateStats[j].txMpdu);
+                EXPECT_EQ(link.peers[i].rate_stats[j].rx_mpdu,
+                          (uint32_t)converted.iface.links[l].peers[i].rateStats[j].rxMpdu);
+                EXPECT_EQ(link.peers[i].rate_stats[j].mpdu_lost,
+                          (uint32_t)converted.iface.links[l].peers[i].rateStats[j].mpduLost);
+                EXPECT_EQ(link.peers[i].rate_stats[j].retries,
+                          (uint32_t)converted.iface.links[l].peers[i].rateStats[j].retries);
+            }
+        }
+        ++l;
+    }  // loop over links
+
+    EXPECT_EQ(legacy_ml_stats.radios.size(), converted.radios.size());
+    for (size_t i = 0; i < legacy_ml_stats.radios.size(); i++) {
+        EXPECT_EQ(legacy_ml_stats.radios[i].stats.radio, converted.radios[i].radioId);
+        EXPECT_EQ(legacy_ml_stats.radios[i].stats.on_time,
+                  (uint32_t)converted.radios[i].onTimeInMs);
+        EXPECT_EQ(legacy_ml_stats.radios[i].stats.tx_time,
+                  (uint32_t)converted.radios[i].txTimeInMs);
+        EXPECT_EQ(legacy_ml_stats.radios[i].stats.rx_time,
+                  (uint32_t)converted.radios[i].rxTimeInMs);
+        EXPECT_EQ(legacy_ml_stats.radios[i].stats.on_time_scan,
+                  (uint32_t)converted.radios[i].onTimeInMsForScan);
+        EXPECT_EQ(legacy_ml_stats.radios[i].tx_time_per_levels.size(),
+                  converted.radios[i].txTimeInMsPerLevel.size());
+        for (size_t j = 0; j < legacy_ml_stats.radios[i].tx_time_per_levels.size(); j++) {
+            EXPECT_EQ(legacy_ml_stats.radios[i].tx_time_per_levels[j],
+                      (uint32_t)converted.radios[i].txTimeInMsPerLevel[j]);
+        }
+        EXPECT_EQ(legacy_ml_stats.radios[i].stats.on_time_nbd,
+                  (uint32_t)converted.radios[i].onTimeInMsForNanScan);
+        EXPECT_EQ(legacy_ml_stats.radios[i].stats.on_time_gscan,
+                  (uint32_t)converted.radios[i].onTimeInMsForBgScan);
+        EXPECT_EQ(legacy_ml_stats.radios[i].stats.on_time_roam_scan,
+                  (uint32_t)converted.radios[i].onTimeInMsForRoamScan);
+        EXPECT_EQ(legacy_ml_stats.radios[i].stats.on_time_pno_scan,
+                  (uint32_t)converted.radios[i].onTimeInMsForPnoScan);
+        EXPECT_EQ(legacy_ml_stats.radios[i].stats.on_time_hs20,
+                  (uint32_t)converted.radios[i].onTimeInMsForHs20Scan);
+        EXPECT_EQ(legacy_ml_stats.radios[i].channel_stats.size(),
+                  converted.radios[i].channelStats.size());
+        for (size_t k = 0; k < legacy_ml_stats.radios[i].channel_stats.size(); k++) {
+            auto& legacy_channel_st = legacy_ml_stats.radios[i].channel_stats[k];
+            EXPECT_EQ(WifiChannelWidthInMhz::WIDTH_20,
+                      converted.radios[i].channelStats[k].channel.width);
+            EXPECT_EQ(legacy_channel_st.channel.center_freq,
+                      converted.radios[i].channelStats[k].channel.centerFreq);
+            EXPECT_EQ(legacy_channel_st.channel.center_freq0,
+                      converted.radios[i].channelStats[k].channel.centerFreq0);
+            EXPECT_EQ(legacy_channel_st.channel.center_freq1,
+                      converted.radios[i].channelStats[k].channel.centerFreq1);
+            EXPECT_EQ(legacy_channel_st.cca_busy_time,
+                      (uint32_t)converted.radios[i].channelStats[k].ccaBusyTimeInMs);
+            EXPECT_EQ(legacy_channel_st.on_time,
+                      (uint32_t)converted.radios[i].channelStats[k].onTimeInMs);
+        }
+    }
+}
+
 TEST_F(AidlStructUtilTest, canConvertLegacyLinkLayerStatsToAidl) {
     legacy_hal::LinkLayerStats legacy_stats{};
     legacy_stats.radios.push_back(legacy_hal::LinkLayerRadioStats{});
@@ -215,78 +494,79 @@
 
     StaLinkLayerStats converted{};
     aidl_struct_util::convertLegacyLinkLayerStatsToAidl(legacy_stats, &converted);
-    EXPECT_EQ(legacy_stats.iface.beacon_rx, (uint32_t)converted.iface.beaconRx);
-    EXPECT_EQ(legacy_stats.iface.rssi_mgmt, converted.iface.avgRssiMgmt);
+    EXPECT_EQ(0, converted.iface.links[0].linkId);
+    EXPECT_EQ(legacy_stats.iface.beacon_rx, (uint32_t)converted.iface.links[0].beaconRx);
+    EXPECT_EQ(legacy_stats.iface.rssi_mgmt, converted.iface.links[0].avgRssiMgmt);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].rx_mpdu,
-              converted.iface.wmeBePktStats.rxMpdu);
+              converted.iface.links[0].wmeBePktStats.rxMpdu);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].tx_mpdu,
-              converted.iface.wmeBePktStats.txMpdu);
+              converted.iface.links[0].wmeBePktStats.txMpdu);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].mpdu_lost,
-              converted.iface.wmeBePktStats.lostMpdu);
+              converted.iface.links[0].wmeBePktStats.lostMpdu);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].retries,
-              converted.iface.wmeBePktStats.retries);
+              converted.iface.links[0].wmeBePktStats.retries);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].contention_time_min,
-              (uint32_t)converted.iface.wmeBeContentionTimeStats.contentionTimeMinInUsec);
+              (uint32_t)converted.iface.links[0].wmeBeContentionTimeStats.contentionTimeMinInUsec);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].contention_time_max,
-              (uint32_t)converted.iface.wmeBeContentionTimeStats.contentionTimeMaxInUsec);
+              (uint32_t)converted.iface.links[0].wmeBeContentionTimeStats.contentionTimeMaxInUsec);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].contention_time_avg,
-              (uint32_t)converted.iface.wmeBeContentionTimeStats.contentionTimeAvgInUsec);
+              (uint32_t)converted.iface.links[0].wmeBeContentionTimeStats.contentionTimeAvgInUsec);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].contention_num_samples,
-              (uint32_t)converted.iface.wmeBeContentionTimeStats.contentionNumSamples);
+              (uint32_t)converted.iface.links[0].wmeBeContentionTimeStats.contentionNumSamples);
 
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].rx_mpdu,
-              converted.iface.wmeBkPktStats.rxMpdu);
+              converted.iface.links[0].wmeBkPktStats.rxMpdu);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].tx_mpdu,
-              converted.iface.wmeBkPktStats.txMpdu);
+              converted.iface.links[0].wmeBkPktStats.txMpdu);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].mpdu_lost,
-              converted.iface.wmeBkPktStats.lostMpdu);
+              converted.iface.links[0].wmeBkPktStats.lostMpdu);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].retries,
-              converted.iface.wmeBkPktStats.retries);
+              converted.iface.links[0].wmeBkPktStats.retries);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].contention_time_min,
-              (uint32_t)converted.iface.wmeBkContentionTimeStats.contentionTimeMinInUsec);
+              (uint32_t)converted.iface.links[0].wmeBkContentionTimeStats.contentionTimeMinInUsec);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].contention_time_max,
-              (uint32_t)converted.iface.wmeBkContentionTimeStats.contentionTimeMaxInUsec);
+              (uint32_t)converted.iface.links[0].wmeBkContentionTimeStats.contentionTimeMaxInUsec);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].contention_time_avg,
-              (uint32_t)converted.iface.wmeBkContentionTimeStats.contentionTimeAvgInUsec);
+              (uint32_t)converted.iface.links[0].wmeBkContentionTimeStats.contentionTimeAvgInUsec);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].contention_num_samples,
-              (uint32_t)converted.iface.wmeBkContentionTimeStats.contentionNumSamples);
+              (uint32_t)converted.iface.links[0].wmeBkContentionTimeStats.contentionNumSamples);
 
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].rx_mpdu,
-              converted.iface.wmeViPktStats.rxMpdu);
+              converted.iface.links[0].wmeViPktStats.rxMpdu);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].tx_mpdu,
-              converted.iface.wmeViPktStats.txMpdu);
+              converted.iface.links[0].wmeViPktStats.txMpdu);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].mpdu_lost,
-              converted.iface.wmeViPktStats.lostMpdu);
+              converted.iface.links[0].wmeViPktStats.lostMpdu);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].retries,
-              converted.iface.wmeViPktStats.retries);
+              converted.iface.links[0].wmeViPktStats.retries);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].contention_time_min,
-              (uint32_t)converted.iface.wmeViContentionTimeStats.contentionTimeMinInUsec);
+              (uint32_t)converted.iface.links[0].wmeViContentionTimeStats.contentionTimeMinInUsec);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].contention_time_max,
-              (uint32_t)converted.iface.wmeViContentionTimeStats.contentionTimeMaxInUsec);
+              (uint32_t)converted.iface.links[0].wmeViContentionTimeStats.contentionTimeMaxInUsec);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].contention_time_avg,
-              (uint32_t)converted.iface.wmeViContentionTimeStats.contentionTimeAvgInUsec);
+              (uint32_t)converted.iface.links[0].wmeViContentionTimeStats.contentionTimeAvgInUsec);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].contention_num_samples,
-              (uint32_t)converted.iface.wmeViContentionTimeStats.contentionNumSamples);
+              (uint32_t)converted.iface.links[0].wmeViContentionTimeStats.contentionNumSamples);
 
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].rx_mpdu,
-              converted.iface.wmeVoPktStats.rxMpdu);
+              converted.iface.links[0].wmeVoPktStats.rxMpdu);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].tx_mpdu,
-              converted.iface.wmeVoPktStats.txMpdu);
+              converted.iface.links[0].wmeVoPktStats.txMpdu);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].mpdu_lost,
-              converted.iface.wmeVoPktStats.lostMpdu);
+              converted.iface.links[0].wmeVoPktStats.lostMpdu);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].retries,
-              converted.iface.wmeVoPktStats.retries);
+              converted.iface.links[0].wmeVoPktStats.retries);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].contention_time_min,
-              (uint32_t)converted.iface.wmeVoContentionTimeStats.contentionTimeMinInUsec);
+              (uint32_t)converted.iface.links[0].wmeVoContentionTimeStats.contentionTimeMinInUsec);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].contention_time_max,
-              (uint32_t)converted.iface.wmeVoContentionTimeStats.contentionTimeMaxInUsec);
+              (uint32_t)converted.iface.links[0].wmeVoContentionTimeStats.contentionTimeMaxInUsec);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].contention_time_avg,
-              (uint32_t)converted.iface.wmeVoContentionTimeStats.contentionTimeAvgInUsec);
+              (uint32_t)converted.iface.links[0].wmeVoContentionTimeStats.contentionTimeAvgInUsec);
     EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].contention_num_samples,
-              (uint32_t)converted.iface.wmeVoContentionTimeStats.contentionNumSamples);
+              (uint32_t)converted.iface.links[0].wmeVoContentionTimeStats.contentionNumSamples);
 
     EXPECT_EQ(legacy_stats.iface.info.time_slicing_duty_cycle_percent,
-              converted.iface.timeSliceDutyCycleInPercent);
+              converted.iface.links[0].timeSliceDutyCycleInPercent);
 
     EXPECT_EQ(legacy_stats.radios.size(), converted.radios.size());
     for (size_t i = 0; i < legacy_stats.radios.size(); i++) {
@@ -331,29 +611,29 @@
         }
     }
 
-    EXPECT_EQ(legacy_stats.peers.size(), converted.iface.peers.size());
+    EXPECT_EQ(legacy_stats.peers.size(), converted.iface.links[0].peers.size());
     for (size_t i = 0; i < legacy_stats.peers.size(); i++) {
         EXPECT_EQ(legacy_stats.peers[i].peer_info.bssload.sta_count,
-                  converted.iface.peers[i].staCount);
+                  converted.iface.links[0].peers[i].staCount);
         EXPECT_EQ(legacy_stats.peers[i].peer_info.bssload.chan_util,
-                  converted.iface.peers[i].chanUtil);
+                  converted.iface.links[0].peers[i].chanUtil);
         for (size_t j = 0; j < legacy_stats.peers[i].rate_stats.size(); j++) {
             EXPECT_EQ(legacy_stats.peers[i].rate_stats[j].rate.preamble,
-                      (uint32_t)converted.iface.peers[i].rateStats[j].rateInfo.preamble);
+                      (uint32_t)converted.iface.links[0].peers[i].rateStats[j].rateInfo.preamble);
             EXPECT_EQ(legacy_stats.peers[i].rate_stats[j].rate.nss,
-                      (uint32_t)converted.iface.peers[i].rateStats[j].rateInfo.nss);
+                      (uint32_t)converted.iface.links[0].peers[i].rateStats[j].rateInfo.nss);
             EXPECT_EQ(legacy_stats.peers[i].rate_stats[j].rate.bw,
-                      (uint32_t)converted.iface.peers[i].rateStats[j].rateInfo.bw);
+                      (uint32_t)converted.iface.links[0].peers[i].rateStats[j].rateInfo.bw);
             EXPECT_EQ(legacy_stats.peers[i].rate_stats[j].rate.rateMcsIdx,
-                      (uint32_t)converted.iface.peers[i].rateStats[j].rateInfo.rateMcsIdx);
+                      (uint32_t)converted.iface.links[0].peers[i].rateStats[j].rateInfo.rateMcsIdx);
             EXPECT_EQ(legacy_stats.peers[i].rate_stats[j].tx_mpdu,
-                      (uint32_t)converted.iface.peers[i].rateStats[j].txMpdu);
+                      (uint32_t)converted.iface.links[0].peers[i].rateStats[j].txMpdu);
             EXPECT_EQ(legacy_stats.peers[i].rate_stats[j].rx_mpdu,
-                      (uint32_t)converted.iface.peers[i].rateStats[j].rxMpdu);
+                      (uint32_t)converted.iface.links[0].peers[i].rateStats[j].rxMpdu);
             EXPECT_EQ(legacy_stats.peers[i].rate_stats[j].mpdu_lost,
-                      (uint32_t)converted.iface.peers[i].rateStats[j].mpduLost);
+                      (uint32_t)converted.iface.links[0].peers[i].rateStats[j].mpduLost);
             EXPECT_EQ(legacy_stats.peers[i].rate_stats[j].retries,
-                      (uint32_t)converted.iface.peers[i].rateStats[j].retries);
+                      (uint32_t)converted.iface.links[0].peers[i].rateStats[j].retries);
         }
     }
 }
diff --git a/wifi/aidl/default/wifi_legacy_hal.cpp b/wifi/aidl/default/wifi_legacy_hal.cpp
index e4883d1..83f368e 100644
--- a/wifi/aidl/default/wifi_legacy_hal.cpp
+++ b/wifi/aidl/default/wifi_legacy_hal.cpp
@@ -119,6 +119,16 @@
     }
 }
 
+// Callback to be invoked for Multi link layer stats results.
+std::function<void((wifi_request_id, wifi_iface_ml_stat*, int, wifi_radio_stat*))>
+        on_link_layer_ml_stats_result_internal_callback;
+void onSyncLinkLayerMlStatsResult(wifi_request_id id, wifi_iface_ml_stat* iface_ml_stat,
+                                  int num_radios, wifi_radio_stat* radio_stat) {
+    if (on_link_layer_ml_stats_result_internal_callback) {
+        on_link_layer_ml_stats_result_internal_callback(id, iface_ml_stat, num_radios, radio_stat);
+    }
+}
+
 // Callback to be invoked for rssi threshold breach.
 std::function<void((wifi_request_id, uint8_t*, int8_t))>
         on_rssi_threshold_breached_internal_callback;
@@ -682,10 +692,44 @@
                                                     &clear_mask_rsp, 1, &stop_rsp);
 }
 
-std::pair<wifi_error, LinkLayerStats> WifiLegacyHal::getLinkLayerStats(
-        const std::string& iface_name) {
-    LinkLayerStats link_stats{};
+// Copies wifi_peer_info* to vector<WifiPeerInfo> and returns poiner to next element.
+wifi_peer_info* WifiLegacyHal::copyPeerInfo(wifi_peer_info* peer_ptr,
+                                            std::vector<WifiPeerInfo> peers) {
+    WifiPeerInfo peer;
+    peer.peer_info = *peer_ptr;
+    if (peer_ptr->num_rate > 0) {
+        // Copy the rate stats.
+        peer.rate_stats.assign(peer_ptr->rate_stats, peer_ptr->rate_stats + peer_ptr->num_rate);
+    }
+    peer.peer_info.num_rate = 0;
+    // Push peer info.
+    peers.push_back(peer);
+    // Return the address of next peer info.
+    return (wifi_peer_info*)((u8*)peer_ptr + sizeof(wifi_peer_info) +
+                             (sizeof(wifi_rate_stat) * peer_ptr->num_rate));
+}
+// Copies wifi_link_stat* to vector<LinkStats> and returns poiner to next element.
+wifi_link_stat* WifiLegacyHal::copyLinkStat(wifi_link_stat* stat_ptr,
+                                            std::vector<LinkStats> stats) {
+    LinkStats linkStat;
+    linkStat.stat = *stat_ptr;
+    wifi_peer_info* l_peer_info_stats_ptr = stat_ptr->peer_info;
+    for (uint32_t i = 0; i < linkStat.stat.num_peers; i++) {
+        l_peer_info_stats_ptr = copyPeerInfo(l_peer_info_stats_ptr, linkStat.peers);
+    }
+    // Copied all peers to linkStat.peers.
+    linkStat.stat.num_peers = 0;
+    // Push link stat.
+    stats.push_back(linkStat);
+    // Read all peers, return the address of next wifi_link_stat.
+    return (wifi_link_stat*)l_peer_info_stats_ptr;
+}
+
+wifi_error WifiLegacyHal::getLinkLayerStats(const std::string& iface_name,
+                                            LinkLayerStats& link_stats,
+                                            LinkLayerMlStats& link_ml_stats) {
     LinkLayerStats* link_stats_ptr = &link_stats;
+    link_stats_ptr->valid = false;
 
     on_link_layer_stats_result_internal_callback = [&link_stats_ptr](
                                                            wifi_request_id /* id */,
@@ -694,6 +738,7 @@
                                                            wifi_radio_stat* radio_stats_ptr) {
         wifi_radio_stat* l_radio_stats_ptr;
         wifi_peer_info* l_peer_info_stats_ptr;
+        link_stats_ptr->valid = true;
 
         if (iface_stats_ptr != nullptr) {
             link_stats_ptr->iface = *iface_stats_ptr;
@@ -751,10 +796,69 @@
         }
     };
 
-    wifi_error status = global_func_table_.wifi_get_link_stats(0, getIfaceHandle(iface_name),
-                                                               {onSyncLinkLayerStatsResult});
+    LinkLayerMlStats* link_ml_stats_ptr = &link_ml_stats;
+    link_ml_stats_ptr->valid = false;
+
+    on_link_layer_ml_stats_result_internal_callback =
+            [this, &link_ml_stats_ptr](wifi_request_id /* id */,
+                                       wifi_iface_ml_stat* iface_ml_stats_ptr, int num_radios,
+                                       wifi_radio_stat* radio_stats_ptr) {
+                wifi_radio_stat* l_radio_stats_ptr;
+                wifi_link_stat* l_link_stat_ptr;
+                link_ml_stats_ptr->valid = true;
+
+                if (iface_ml_stats_ptr != nullptr && iface_ml_stats_ptr->num_links > 0) {
+                    // Copy stats from wifi_iface_ml_stat to LinkLayerMlStats,
+                    //  - num_links * links[] to vector of links.
+                    //  - num_peers * peer_info[] to vector of links[i].peers.
+                    link_ml_stats_ptr->iface = *iface_ml_stats_ptr;
+                    l_link_stat_ptr = iface_ml_stats_ptr->links;
+                    for (int l = 0; l < iface_ml_stats_ptr->num_links; ++l) {
+                        l_link_stat_ptr = copyLinkStat(l_link_stat_ptr, link_ml_stats_ptr->links);
+                    }
+                } else {
+                    LOG(ERROR) << "Invalid iface stats in link layer stats";
+                }
+                if (num_radios <= 0 || radio_stats_ptr == nullptr) {
+                    LOG(ERROR) << "Invalid radio stats in link layer stats";
+                    return;
+                }
+                l_radio_stats_ptr = radio_stats_ptr;
+                for (int i = 0; i < num_radios; i++) {
+                    LinkLayerRadioStats radio;
+
+                    radio.stats = *l_radio_stats_ptr;
+                    // Copy over the tx level array to the separate vector.
+                    if (l_radio_stats_ptr->num_tx_levels > 0 &&
+                        l_radio_stats_ptr->tx_time_per_levels != nullptr) {
+                        radio.tx_time_per_levels.assign(l_radio_stats_ptr->tx_time_per_levels,
+                                                        l_radio_stats_ptr->tx_time_per_levels +
+                                                                l_radio_stats_ptr->num_tx_levels);
+                    }
+                    radio.stats.num_tx_levels = 0;
+                    radio.stats.tx_time_per_levels = nullptr;
+                    /* Copy over the channel stat to separate vector */
+                    if (l_radio_stats_ptr->num_channels > 0) {
+                        /* Copy the channel stats */
+                        radio.channel_stats.assign(
+                                l_radio_stats_ptr->channels,
+                                l_radio_stats_ptr->channels + l_radio_stats_ptr->num_channels);
+                    }
+                    link_ml_stats_ptr->radios.push_back(radio);
+                    l_radio_stats_ptr =
+                            (wifi_radio_stat*)((u8*)l_radio_stats_ptr + sizeof(wifi_radio_stat) +
+                                               (sizeof(wifi_channel_stat) *
+                                                l_radio_stats_ptr->num_channels));
+                }
+            };
+
+    wifi_error status = global_func_table_.wifi_get_link_stats(
+            0, getIfaceHandle(iface_name),
+            {onSyncLinkLayerStatsResult, onSyncLinkLayerMlStatsResult});
     on_link_layer_stats_result_internal_callback = nullptr;
-    return {status, link_stats};
+    on_link_layer_ml_stats_result_internal_callback = nullptr;
+
+    return status;
 }
 
 wifi_error WifiLegacyHal::startRssiMonitoring(
@@ -1624,6 +1728,7 @@
     on_gscan_event_internal_callback = nullptr;
     on_gscan_full_result_internal_callback = nullptr;
     on_link_layer_stats_result_internal_callback = nullptr;
+    on_link_layer_ml_stats_result_internal_callback = nullptr;
     on_rssi_threshold_breached_internal_callback = nullptr;
     on_ring_buffer_data_internal_callback = nullptr;
     on_error_alert_internal_callback = nullptr;
diff --git a/wifi/aidl/default/wifi_legacy_hal.h b/wifi/aidl/default/wifi_legacy_hal.h
index 33ed359..c40118e 100644
--- a/wifi/aidl/default/wifi_legacy_hal.h
+++ b/wifi/aidl/default/wifi_legacy_hal.h
@@ -363,7 +363,21 @@
     wifi_iface_stat iface;
     std::vector<LinkLayerRadioStats> radios;
     std::vector<WifiPeerInfo> peers;
+    bool valid;
 };
+
+struct LinkStats {
+    wifi_link_stat stat;
+    std::vector<WifiPeerInfo> peers;
+};
+
+struct LinkLayerMlStats {
+    wifi_iface_ml_stat iface;
+    std::vector<LinkStats> links;
+    std::vector<LinkLayerRadioStats> radios;
+    bool valid;
+};
+
 #pragma GCC diagnostic pop
 
 // The |WLAN_DRIVER_WAKE_REASON_CNT.cmd_event_wake_cnt| and
@@ -537,7 +551,9 @@
     // Link layer stats functions.
     wifi_error enableLinkLayerStats(const std::string& iface_name, bool debug);
     wifi_error disableLinkLayerStats(const std::string& iface_name);
-    std::pair<wifi_error, LinkLayerStats> getLinkLayerStats(const std::string& iface_name);
+    wifi_error getLinkLayerStats(const std::string& iface_name,
+                                 legacy_hal::LinkLayerStats& legacy_stats,
+                                 legacy_hal::LinkLayerMlStats& legacy_ml_stats);
     // RSSI monitor functions.
     wifi_error startRssiMonitoring(
             const std::string& iface_name, wifi_request_id id, int8_t max_rssi, int8_t min_rssi,
@@ -710,6 +726,8 @@
     // Handles wifi (error) status of Virtual interface create/delete
     wifi_error handleVirtualInterfaceCreateOrDeleteStatus(const std::string& ifname,
                                                           wifi_error status);
+    wifi_link_stat* copyLinkStat(wifi_link_stat* stat_ptr, std::vector<LinkStats> stats);
+    wifi_peer_info* copyPeerInfo(wifi_peer_info* peer_ptr, std::vector<WifiPeerInfo> peers);
 
     // Global function table of legacy HAL.
     wifi_hal_fn global_func_table_;
diff --git a/wifi/aidl/default/wifi_sta_iface.cpp b/wifi/aidl/default/wifi_sta_iface.cpp
index 57384bf..08ed9d2 100644
--- a/wifi/aidl/default/wifi_sta_iface.cpp
+++ b/wifi/aidl/default/wifi_sta_iface.cpp
@@ -404,13 +404,22 @@
 
 std::pair<StaLinkLayerStats, ndk::ScopedAStatus> WifiStaIface::getLinkLayerStatsInternal() {
     legacy_hal::wifi_error legacy_status;
-    legacy_hal::LinkLayerStats legacy_stats;
-    std::tie(legacy_status, legacy_stats) = legacy_hal_.lock()->getLinkLayerStats(ifname_);
+    legacy_hal::LinkLayerStats legacy_stats{};
+    legacy_hal::LinkLayerMlStats legacy_ml_stats{};
+    legacy_status = legacy_hal_.lock()->getLinkLayerStats(ifname_, legacy_stats, legacy_ml_stats);
     if (legacy_status != legacy_hal::WIFI_SUCCESS) {
         return {StaLinkLayerStats{}, createWifiStatusFromLegacyError(legacy_status)};
     }
     StaLinkLayerStats aidl_stats;
-    if (!aidl_struct_util::convertLegacyLinkLayerStatsToAidl(legacy_stats, &aidl_stats)) {
+    if (legacy_stats.valid) {
+        if (!aidl_struct_util::convertLegacyLinkLayerStatsToAidl(legacy_stats, &aidl_stats)) {
+            return {StaLinkLayerStats{}, createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
+        }
+    } else if (legacy_ml_stats.valid) {
+        if (!aidl_struct_util::convertLegacyLinkLayerMlStatsToAidl(legacy_ml_stats, &aidl_stats)) {
+            return {StaLinkLayerStats{}, createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
+        }
+    } else {
         return {StaLinkLayerStats{}, createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
     }
     return {aidl_stats, ndk::ScopedAStatus::ok()};
diff --git a/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp b/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
index ef7e274..5e55315 100644
--- a/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
+++ b/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
@@ -145,7 +145,7 @@
     std::shared_ptr<IWifiStaIface> iface;
     auto status = createStaIface(&iface);
     if (status.isOk()) {
-        EXPECT_GT(link_layer_stats.iface.timeSliceDutyCycleInPercent, 0);
+        EXPECT_GT(link_layer_stats.iface.links[0].timeSliceDutyCycleInPercent, 0);
     }
 
     // Disable link layer stats collection.