wifi: Add Support for Multi-Link Layer stats in shim layer

With MLO, WiFi 7 supports establishing multiple links between the
Station and Access Point. Each of the link has its own stats.

To maintain backward compatibility, if multi link stats is not supported
by vendor, always fallback to single link layer stats.

Bug: 246988155
Test: hardware/interfaces/wifi/aidl/default/tests/runtests.sh
Change-Id: Iede8ce771f0e58975222808b1870e5e2e179384c
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/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()};