Merge "Move desktop variables to the right place" into main
diff --git a/apex/Android.bp b/apex/Android.bp
index 5fca381..6aad837 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -6,6 +6,19 @@
     installable: false,
 }
 
+genrule {
+    name: "com.android.hardware.hostapd.rc-gen",
+    srcs: ["android.hardware.hostapd.rc"],
+    out: ["com.android.hardware.hostapd.rc"],
+    cmd: "sed -E 's@/vendor/bin@/apex/com.android.hardware.wpa_supplicant/bin@' $(in) > $(out)",
+}
+
+prebuilt_etc {
+    name: "com.android.hardware.hostapd.rc",
+    src: ":com.android.hardware.hostapd.rc-gen",
+    installable: false,
+}
+
 apex {
     name: "com.android.hardware.wpa_supplicant",
     manifest: "apex_manifest.json",
diff --git a/apex/android.hardware.hostapd.rc b/apex/android.hardware.hostapd.rc
new file mode 100644
index 0000000..d89752e
--- /dev/null
+++ b/apex/android.hardware.hostapd.rc
@@ -0,0 +1,21 @@
+#
+# init.rc fragment for hostapd on Android
+# Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+#
+
+on property:apex.all.ready=true
+    mkdir /data/vendor/wifi 0770 wifi wifi
+    mkdir /data/vendor/wifi/hostapd 0770 wifi wifi
+    mkdir /data/vendor/wifi/hostapd/sockets 0770 wifi wifi
+
+service hostapd /vendor/bin/hw/hostapd
+    interface aidl android.hardware.wifi.hostapd.IHostapd/default
+    class main
+    capabilities NET_ADMIN NET_RAW
+    user wifi
+    group wifi net_raw net_admin
+    disabled
+    oneshot
diff --git a/board_config_wpa_supplicant.mk b/board_config_wpa_supplicant.mk
new file mode 100644
index 0000000..c03f94a
--- /dev/null
+++ b/board_config_wpa_supplicant.mk
@@ -0,0 +1,96 @@
+#
+# Copyright (C) 2024 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 adds wpa_supplicant_8 variables into soong config namespace (`wpa_supplicant_8`)
+# ###############################################################
+
+ifdef BOARD_HOSTAPD_DRIVER
+$(call soong_config_set_bool,wpa_supplicant_8,wpa_build_hostapd,true)
+ifneq ($(BOARD_HOSTAPD_DRIVER),NL80211)
+    $(error BOARD_HOSTAPD_DRIVER set to $(BOARD_HOSTAPD_DRIVER) but current soong expected it should be NL80211 only!)
+endif
+endif
+
+ifdef BOARD_WPA_SUPPLICANT_DRIVER
+ifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),NL80211)
+    $(error BOARD_WPA_SUPPLICANT_DRIVER set to $(BOARD_WPA_SUPPLICANT_DRIVER) but current soong expected it should be NL80211 only!)
+endif
+endif
+
+# This is for CONFIG_DRIVER_NL80211_BRCM, CONFIG_DRIVER_NL80211_SYNA, CONFIG_DRIVER_NL80211_QCA
+# And it is only used for a cflags setting in driver.
+$(call soong_config_set,wpa_supplicant_8,board_wlan_device,$(BOARD_WLAN_DEVICE))
+
+# Belong to CONFIG_IEEE80211AX definition
+ifeq ($(WIFI_FEATURE_HOSTAPD_11AX),true)
+$(call soong_config_set_bool,wpa_supplicant_8,hostapd_11ax,true)
+endif
+
+ifeq ($(WIFI_FEATURE_SUPPLICANT_11AX),true)
+$(call soong_config_set_bool,wpa_supplicant_8,wpa_supplicant_11ax,true)
+endif
+
+# Belong to CONFIG_IEEE80211BE definition
+ifeq ($(WIFI_FEATURE_HOSTAPD_11BE),true)
+$(call soong_config_set_bool,wpa_supplicant_8,hostapd_11be,true)
+endif
+
+ifeq ($(WIFI_FEATURE_SUPPLICANT_11BE),true)
+$(call soong_config_set_bool,wpa_supplicant_8,wpa_supplicant_11be,true)
+endif
+
+# PLATFORM_VERSION
+$(call soong_config_set,wpa_supplicant_8,platform_version,$(PLATFORM_VERSION))
+
+# BOARD_HOSTAPD_PRIVATE_LIB
+ifeq ($(BOARD_HOSTAPD_PRIVATE_LIB),)
+$(call soong_config_set_bool,wpa_supplicant_8,hostapd_use_stub_lib,true)
+else
+$(call soong_config_set,wpa_supplicant_8,board_hostapd_private_lib,$(BOARD_HOSTAPD_PRIVATE_LIB))
+endif
+
+ifeq ($(BOARD_HOSTAPD_CONFIG_80211W_MFP_OPTIONAL),true)
+$(call soong_config_set_bool,wpa_supplicant_8,board_hostapd_config_80211w_mfp_optional,true)
+endif
+
+ifneq ($(BOARD_HOSTAPD_PRIVATE_LIB_EVENT),)
+$(call soong_config_set_bool,wpa_supplicant_8,board_hostapd_private_lib_event,true)
+endif
+
+# BOARD_WPA_SUPPLICANT_PRIVATE_LIB
+ifeq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),)
+$(call soong_config_set_bool,wpa_supplicant_8,wpa_supplicant_use_stub_lib,true)
+else
+$(call soong_config_set,wpa_supplicant_8,board_wpa_supplicant_private_lib,$(BOARD_WPA_SUPPLICANT_PRIVATE_LIB))
+endif
+
+ifneq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB_EVENT),)
+$(call soong_config_set_bool,wpa_supplicant_8,board_wpa_supplicant_private_lib_event,true)
+endif
+
+ifeq ($(WIFI_PRIV_CMD_UPDATE_MBO_CELL_STATUS), enabled)
+$(call soong_config_set_bool,wpa_supplicant_8,wifi_priv_cmd_update_mbo_cell_status,true)
+endif
+
+ifeq ($(WIFI_HIDL_UNIFIED_SUPPLICANT_SERVICE_RC_ENTRY), true)
+$(call soong_config_set_bool,wpa_supplicant_8,wifi_hidl_unified_supplicant_service_rc_entry,true)
+endif
+
+# New added in internal main
+ifeq ($(WIFI_BRCM_OPEN_SOURCE_MULTI_AKM), enabled)
+$(call soong_config_set_bool,wpa_supplicant_8,wifi_brcm_open_source_multi_akm,true)
+endif
diff --git a/hostapd/Android.bp b/hostapd/Android.bp
index 4753644..533a917 100644
--- a/hostapd/Android.bp
+++ b/hostapd/Android.bp
@@ -743,16 +743,3 @@
 }
 
 // End of non-cuttlefish section
-
-genrule {
-    name: "com.android.hardware.hostapd.rc-gen",
-    srcs: ["hostapd.android.rc"],
-    out: ["com.android.hardware.hostapd.rc"],
-    cmd: "sed -E 's@/vendor/bin@/apex/com.android.hardware.wpa_supplicant/bin@' $(in) > $(out)",
-}
-
-prebuilt_etc {
-    name: "com.android.hardware.hostapd.rc",
-    src: ":com.android.hardware.hostapd.rc-gen",
-    installable: false,
-}
diff --git a/wpa_supplicant/Android.bp b/wpa_supplicant/Android.bp
index 599ce3a..5a08fa1 100644
--- a/wpa_supplicant/Android.bp
+++ b/wpa_supplicant/Android.bp
@@ -1421,6 +1421,7 @@
         "wpa_supplicant_mainline_srcs_default",
         "wpa_supplicant_includes_default",
         "wpa_supplicant_mainline_cflags_default",
+        "wpa_supplicant_usd_defaults",
     ],
     apex_available: [
         "//apex_available:platform",
@@ -1459,6 +1460,7 @@
         "wpa_supplicant_includes_default",
         "wpa_supplicant_mainline_cflags_default",
         "wpa_supplicant_mainline_srcs_default",
+        "wpa_supplicant_usd_defaults",
     ],
     shared_libs: [
         "android.system.wifi.mainline_supplicant-ndk",
diff --git a/wpa_supplicant/aidl/mainline/Android.bp b/wpa_supplicant/aidl/mainline/Android.bp
index a2f5370..43c9c1a 100644
--- a/wpa_supplicant/aidl/mainline/Android.bp
+++ b/wpa_supplicant/aidl/mainline/Android.bp
@@ -31,6 +31,7 @@
         "libbinder_ndk",
     ],
     cppflags: [
+        "-DMAINLINE_SUPPLICANT",
         "-Wall",
         "-Werror",
         "-Wno-unused-parameter",
diff --git a/wpa_supplicant/aidl/mainline/callback_bridge.cpp b/wpa_supplicant/aidl/mainline/callback_bridge.cpp
new file mode 100644
index 0000000..a7b284b
--- /dev/null
+++ b/wpa_supplicant/aidl/mainline/callback_bridge.cpp
@@ -0,0 +1,100 @@
+/*
+ * WPA Supplicant - Interface to receive callbacks from the core supplicant
+ * Copyright (c) 2025, Google Inc. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils.h"
+#include "usd_utils.h"
+
+extern "C"
+{
+#include "callback_bridge.h"
+}
+
+using ::aidl::android::system::wifi::mainline_supplicant::UsdMessageInfo;
+
+void mainline_aidl_notify_usd_service_discovered(struct wpa_supplicant *wpa_s,
+        enum nan_service_protocol_type srv_proto_type,
+        int subscribe_id, int peer_publish_id, const u8 *peer_addr,
+        bool fsd, const u8 *ssi, size_t ssi_len) {
+    if (!wpa_s || !peer_addr)
+        return;
+
+    auto callback = getStaIfaceCallback(wpa_s->ifname);
+    if (!callback)
+        return;
+
+    wpa_printf(MSG_DEBUG, "Notifying USD service discovered");
+    auto serviceDiscoveryInfo = createUsdServiceDiscoveryInfo(
+        srv_proto_type, subscribe_id, peer_publish_id, peer_addr, fsd, ssi, ssi_len);
+    callback->onUsdServiceDiscovered(serviceDiscoveryInfo);
+}
+
+void mainline_aidl_notify_usd_publish_replied(struct wpa_supplicant *wpa_s,
+        enum nan_service_protocol_type srv_proto_type,
+        int publish_id, int peer_subscribe_id,
+        const u8 *peer_addr, const u8 *ssi, size_t ssi_len) {
+    if (!wpa_s || !peer_addr)
+        return;
+
+    auto callback = getStaIfaceCallback(wpa_s->ifname);
+    if (!callback)
+        return;
+
+    wpa_printf(MSG_DEBUG, "Notifying USD publish replied");
+    auto serviceDiscoveryInfo = createUsdServiceDiscoveryInfo(
+        srv_proto_type, publish_id, peer_subscribe_id, peer_addr, false /* fsd */,
+        ssi, ssi_len);
+    callback->onUsdPublishReplied(serviceDiscoveryInfo);
+}
+
+void mainline_aidl_notify_usd_message_received(struct wpa_supplicant *wpa_s, int id,
+        int peer_instance_id, const u8 *peer_addr,
+        const u8 *message, size_t message_len) {
+    if (!wpa_s || !peer_addr)
+        return;
+
+    auto callback = getStaIfaceCallback(wpa_s->ifname);
+    if (!callback)
+        return;
+
+    UsdMessageInfo messageInfo;
+    messageInfo.ownId = id;
+    messageInfo.peerId = peer_instance_id;
+    messageInfo.peerMacAddress = macAddrBytesToArray(peer_addr);
+    messageInfo.message = message ? byteArrToVec(message, message_len) : std::vector<uint8_t>();
+
+    wpa_printf(MSG_DEBUG, "Notifying USD message received");
+    callback->onUsdMessageReceived(messageInfo);
+}
+
+void mainline_aidl_notify_usd_publish_terminated(struct wpa_supplicant *wpa_s,
+        int publish_id, enum nan_de_reason reason) {
+    if (!wpa_s)
+        return;
+
+    auto callback = getStaIfaceCallback(wpa_s->ifname);
+    if (!callback)
+        return;
+
+    wpa_printf(MSG_DEBUG, "Notifying USD publish terminated");
+    callback->onUsdPublishTerminated(
+        publish_id, convertInternalUsdTerminateReasonCodeToAidl(reason));
+}
+
+void mainline_aidl_notify_usd_subscribe_terminated(struct wpa_supplicant *wpa_s,
+        int subscribe_id, enum nan_de_reason reason) {
+    if (!wpa_s)
+        return;
+
+    auto callback = getStaIfaceCallback(wpa_s->ifname);
+    if (!callback)
+        return;
+
+    wpa_printf(MSG_DEBUG, "Notifying USD subscribe terminated");
+    callback->onUsdSubscribeTerminated(
+        subscribe_id, convertInternalUsdTerminateReasonCodeToAidl(reason));
+}
diff --git a/wpa_supplicant/aidl/mainline/callback_bridge.h b/wpa_supplicant/aidl/mainline/callback_bridge.h
new file mode 100644
index 0000000..c35bf37
--- /dev/null
+++ b/wpa_supplicant/aidl/mainline/callback_bridge.h
@@ -0,0 +1,70 @@
+/*
+ * WPA Supplicant - Interface to receive callbacks from the core supplicant
+ * Copyright (c) 2025, Google Inc. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MAINLINE_SUPPLICANT_CALLBACK_BRIDGE_H
+#define MAINLINE_SUPPLICANT_CALLBACK_BRIDGE_H
+
+/**
+ * Intermediary layer to receive callbacks from the core supplicant.
+ *
+ * For each callback, we provide a full implementation if the MAINLINE_SUPPLICANT
+ * flag is enabled. Otherwise, we provide an empty default implementation for builds
+ * that do not have the flag enabled (ex. the vendor supplicant).
+ */
+#ifdef _cplusplus
+extern "C"
+{
+#endif  // _cplusplus
+
+#include "utils/common.h"
+#include "src/common/nan_de.h"
+#include "wpa_supplicant_i.h"
+
+#ifdef MAINLINE_SUPPLICANT
+
+void mainline_aidl_notify_usd_service_discovered(struct wpa_supplicant *wpa_s,
+    enum nan_service_protocol_type srv_proto_type,
+    int subscribe_id, int peer_publish_id, const u8 *peer_addr,
+    bool fsd, const u8 *ssi, size_t ssi_len);
+void mainline_aidl_notify_usd_publish_replied(struct wpa_supplicant *wpa_s,
+    enum nan_service_protocol_type srv_proto_type,
+    int publish_id, int peer_subscribe_id,
+    const u8 *peer_addr, const u8 *ssi, size_t ssi_len);
+void mainline_aidl_notify_usd_message_received(struct wpa_supplicant *wpa_s, int id,
+    int peer_instance_id, const u8 *peer_addr,
+    const u8 *message, size_t message_len);
+void mainline_aidl_notify_usd_publish_terminated(struct wpa_supplicant *wpa_s,
+    int publish_id, enum nan_de_reason reason);
+void mainline_aidl_notify_usd_subscribe_terminated(struct wpa_supplicant *wpa_s,
+    int subscribe_id, enum nan_de_reason reason);
+
+#else // MAINLINE_SUPPLICANT
+
+static void mainline_aidl_notify_usd_service_discovered(struct wpa_supplicant *wpa_s,
+    enum nan_service_protocol_type srv_proto_type,
+    int subscribe_id, int peer_publish_id, const u8 *peer_addr,
+    bool fsd, const u8 *ssi, size_t ssi_len) {}
+static void mainline_aidl_notify_usd_publish_replied(struct wpa_supplicant *wpa_s,
+    enum nan_service_protocol_type srv_proto_type,
+    int publish_id, int peer_subscribe_id,
+    const u8 *peer_addr, const u8 *ssi, size_t ssi_len) {}
+static void mainline_aidl_notify_usd_message_received(struct wpa_supplicant *wpa_s, int id,
+    int peer_instance_id, const u8 *peer_addr,
+    const u8 *message, size_t message_len) {}
+static void mainline_aidl_notify_usd_publish_terminated(struct wpa_supplicant *wpa_s,
+    int publish_id, enum nan_de_reason reason) {}
+static void mainline_aidl_notify_usd_subscribe_terminated(struct wpa_supplicant *wpa_s,
+    int subscribe_id, enum nan_de_reason reason) {}
+
+#endif // MAINLINE_SUPPLICANT
+
+#ifdef _cplusplus
+}
+#endif  // _cplusplus
+
+#endif // MAINLINE_SUPPLICANT_CALLBACK_BRIDGE_H
diff --git a/wpa_supplicant/aidl/mainline/callback_manager.cpp b/wpa_supplicant/aidl/mainline/callback_manager.cpp
new file mode 100644
index 0000000..4922ce1
--- /dev/null
+++ b/wpa_supplicant/aidl/mainline/callback_manager.cpp
@@ -0,0 +1,90 @@
+/*
+ * WPA Supplicant - Manager for callback objects
+ * Copyright (c) 2025, Google Inc. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "callback_manager.h"
+
+extern "C"
+{
+#include "utils/common.h"
+#include "wpa_supplicant_i.h"
+}
+
+// Raw pointer to the global structure maintained by the core
+// Declared here to be accessible to onDeath()
+struct wpa_global* wpa_global_;
+
+void onDeath(void* cookie) {
+    wpa_printf(MSG_ERROR, "Client died. Terminating...");
+    wpa_supplicant_terminate_proc(wpa_global_);
+}
+
+CallbackManager* CallbackManager::instance_ = NULL;
+
+void CallbackManager::initialize(struct wpa_global* wpa_global) {
+    wpa_printf(MSG_INFO, "Initializing the callback manager");
+    wpa_global_ = wpa_global;
+    instance_ = new CallbackManager();
+    instance_->death_notifier_ = AIBinder_DeathRecipient_new(onDeath);
+}
+
+CallbackManager* CallbackManager::getInstance() {
+    return instance_;
+}
+
+bool CallbackManager::registerStaIfaceCallback(std::string ifaceName,
+        const std::shared_ptr<IStaInterfaceCallback>& callback) {
+    std::lock_guard<std::mutex> guard(mutex_);
+    if (!callback) {
+         wpa_printf(MSG_ERROR, "Attempted to register a null callback for STA iface %s",
+            ifaceName.c_str());
+        return false;
+    }
+    if (callbackRegisteredForStaIface(ifaceName)) {
+        wpa_printf(MSG_ERROR, "Callback is already registered for STA iface %s",
+            ifaceName.c_str());
+        return false;
+    }
+    binder_status_t status = AIBinder_linkToDeath(callback->asBinder().get(),
+        death_notifier_, nullptr /* cookie */);
+    if (status != STATUS_OK) {
+        wpa_printf(MSG_ERROR, "Received code %d when linking death recipient"
+            " for callback on STA iface %s", status, ifaceName.c_str());
+        return false;
+    }
+    wpa_printf(MSG_INFO, "Registered callback for STA iface %s", ifaceName.c_str());
+    sta_iface_callbacks_[ifaceName] = callback;
+    return true;
+}
+
+void CallbackManager::unregisterStaIfaceCallback(std::string ifaceName) {
+    std::lock_guard<std::mutex> guard(mutex_);
+    wpa_printf(MSG_INFO, "Unregistering callback for STA iface %s",
+        ifaceName.c_str());
+    if (!callbackRegisteredForStaIface(ifaceName)) {
+        wpa_printf(MSG_INFO, "Callback does not need to be unregistered"
+            " for STA iface %s", ifaceName.c_str());
+        return;
+    }
+    auto callback = sta_iface_callbacks_[ifaceName];
+    binder_status_t status = AIBinder_unlinkToDeath(callback->asBinder().get(),
+        death_notifier_, nullptr /* cookie */);
+    if (status != STATUS_OK) {
+        wpa_printf(MSG_ERROR, "Received code %d when unlinking death recipient"
+            " for callback on STA iface %s", status, ifaceName.c_str());
+    }
+    sta_iface_callbacks_.erase(ifaceName);
+}
+
+std::shared_ptr<IStaInterfaceCallback> CallbackManager::getStaIfaceCallback(
+        std::string ifaceName) {
+    std::lock_guard<std::mutex> guard(mutex_);
+    if (!callbackRegisteredForStaIface(ifaceName)) {
+        return nullptr;
+    }
+    return sta_iface_callbacks_[ifaceName];
+}
diff --git a/wpa_supplicant/aidl/mainline/callback_manager.h b/wpa_supplicant/aidl/mainline/callback_manager.h
new file mode 100644
index 0000000..788c7b3
--- /dev/null
+++ b/wpa_supplicant/aidl/mainline/callback_manager.h
@@ -0,0 +1,52 @@
+/*
+ * WPA Supplicant - Manager for callback objects
+ * Copyright (c) 2025, Google Inc. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MAINLINE_SUPPLICANT_CALLBACK_MANAGER_H
+#define MAINLINE_SUPPLICANT_CALLBACK_MANAGER_H
+
+#include <map>
+#include <mutex>
+#include <string>
+
+#include <aidl/android/system/wifi/mainline_supplicant/IStaInterfaceCallback.h>
+
+using ::aidl::android::system::wifi::mainline_supplicant::IStaInterfaceCallback;
+
+/**
+ * Class to manage all registered callback objects.
+ *
+ * On startup, a singleton instance should be created using initialize().
+ * Subsequent callers should retrieve the singleton using getInstance().
+ */
+class CallbackManager {
+    public:
+        // Singleton access
+        static void initialize(struct wpa_global* wpa_global);
+        static CallbackManager* getInstance();
+
+        // Member functions
+        bool registerStaIfaceCallback(std::string ifaceName,
+            const std::shared_ptr<IStaInterfaceCallback>& callback);
+        void unregisterStaIfaceCallback(std::string ifaceName);
+        std::shared_ptr<IStaInterfaceCallback> getStaIfaceCallback(std::string ifaceName);
+
+    private:
+        inline bool callbackRegisteredForStaIface(std::string ifaceName) {
+            return sta_iface_callbacks_.find(ifaceName) != sta_iface_callbacks_.end();
+        }
+
+        // Singleton instance of this class
+        static CallbackManager* instance_;
+
+        // Member variables
+        std::mutex mutex_;
+        AIBinder_DeathRecipient* death_notifier_;
+        std::map<std::string, std::shared_ptr<IStaInterfaceCallback>> sta_iface_callbacks_;
+};
+
+#endif // MAINLINE_SUPPLICANT_CALLBACK_MANAGER_H
diff --git a/wpa_supplicant/aidl/mainline/mainline_supplicant.cpp b/wpa_supplicant/aidl/mainline/mainline_supplicant.cpp
index dd2babe..ff5c388 100644
--- a/wpa_supplicant/aidl/mainline/mainline_supplicant.cpp
+++ b/wpa_supplicant/aidl/mainline/mainline_supplicant.cpp
@@ -7,6 +7,7 @@
  */
 
 #include "aidl/shared/shared_utils.h"
+#include "callback_manager.h"
 #include "mainline_supplicant.h"
 #include "utils.h"
 
@@ -18,14 +19,17 @@
     wpa_global_ = global;
 }
 
-ndk::ScopedAStatus MainlineSupplicant::addUsdInterface(const std::string& ifaceName) {
+ndk::ScopedAStatus MainlineSupplicant::addStaInterface(const std::string& ifaceName,
+        std::shared_ptr<IStaInterface>* _aidl_return) {
     if (ifaceName.empty()) {
         wpa_printf(MSG_ERROR, "Empty iface name provided");
         return createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID);
     }
 
-    if (active_usd_ifaces_.find(ifaceName) != active_usd_ifaces_.end()) {
+    if (active_sta_ifaces_.find(ifaceName) != active_sta_ifaces_.end()) {
         wpa_printf(MSG_INFO, "Interface %s already exists", ifaceName.c_str());
+        std::shared_ptr<IStaInterface> staIface = active_sta_ifaces_[ifaceName];
+        _aidl_return = &staIface;
         return ndk::ScopedAStatus::ok();
     }
 
@@ -46,18 +50,22 @@
         return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
     }
 
+    std::shared_ptr<IStaInterface> staIface =
+        ndk::SharedRefBase::make<StaIface>(wpa_global_, ifaceName);
+    active_sta_ifaces_[ifaceName] = staIface;
+    *_aidl_return = staIface;
+
     wpa_printf(MSG_INFO, "Interface %s was added successfully", ifaceName.c_str());
-    active_usd_ifaces_.insert(ifaceName);
     return ndk::ScopedAStatus::ok();
 }
 
-ndk::ScopedAStatus MainlineSupplicant::removeUsdInterface(const std::string& ifaceName) {
+ndk::ScopedAStatus MainlineSupplicant::removeStaInterface(const std::string& ifaceName) {
     if (ifaceName.empty()) {
         wpa_printf(MSG_ERROR, "Empty iface name provided");
         return createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID);
     }
 
-    if (active_usd_ifaces_.find(ifaceName) == active_usd_ifaces_.end()) {
+    if (active_sta_ifaces_.find(ifaceName) == active_sta_ifaces_.end()) {
         wpa_printf(MSG_ERROR, "Interface %s does not exist", ifaceName.c_str());
         return createStatus(SupplicantStatusCode::FAILURE_IFACE_UNKNOWN);
     }
@@ -73,8 +81,13 @@
         return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
     }
 
+    // Remove interface and callback from the internal maps
+    CallbackManager* callbackManager = CallbackManager::getInstance();
+    WPA_ASSERT(callbackManager);
+    callbackManager->unregisterStaIfaceCallback(ifaceName);
+    active_sta_ifaces_.erase(ifaceName);
+
     wpa_printf(MSG_INFO, "Interface %s was removed successfully", ifaceName.c_str());
-    active_usd_ifaces_.erase(ifaceName);
     return ndk::ScopedAStatus::ok();
 }
 
diff --git a/wpa_supplicant/aidl/mainline/mainline_supplicant.h b/wpa_supplicant/aidl/mainline/mainline_supplicant.h
index 38a355f..fea7c73 100644
--- a/wpa_supplicant/aidl/mainline/mainline_supplicant.h
+++ b/wpa_supplicant/aidl/mainline/mainline_supplicant.h
@@ -9,9 +9,12 @@
 #ifndef MAINLINE_SUPPLICANT_IMPL_H
 #define MAINLINE_SUPPLICANT_IMPL_H
 
-#include <set>
+#include <map>
+
+#include "sta_iface.h"
 
 #include <aidl/android/system/wifi/mainline_supplicant/BnMainlineSupplicant.h>
+#include <aidl/android/system/wifi/mainline_supplicant/IStaInterface.h>
 #include <aidl/android/system/wifi/mainline_supplicant/SupplicantStatusCode.h>
 
 extern "C"
@@ -24,20 +27,22 @@
 }
 
 using ::aidl::android::system::wifi::mainline_supplicant::BnMainlineSupplicant;
+using ::aidl::android::system::wifi::mainline_supplicant::IStaInterface;
 using ::aidl::android::system::wifi::mainline_supplicant::SupplicantStatusCode;
 
 class MainlineSupplicant : public BnMainlineSupplicant {
     public:
         MainlineSupplicant(struct wpa_global* global);
-        ndk::ScopedAStatus addUsdInterface(const std::string& ifaceName);
-        ndk::ScopedAStatus removeUsdInterface(const std::string& ifaceName);
+        ndk::ScopedAStatus addStaInterface(const std::string& ifaceName,
+            std::shared_ptr<IStaInterface>* _aidl_return);
+        ndk::ScopedAStatus removeStaInterface(const std::string& ifaceName);
         ndk::ScopedAStatus terminate();
 
     private:
         // Raw pointer to the global structure maintained by the core
         struct wpa_global* wpa_global_;
-        // Names of all active USD interfaces
-        std::set<std::string> active_usd_ifaces_;
+        // Map containing all active STA interfaces, mapped by iface name -> object
+        std::map<std::string, std::shared_ptr<IStaInterface>> active_sta_ifaces_;
 };
 
 #endif  // MAINLINE_SUPPLICANT_IMPL_H
diff --git a/wpa_supplicant/aidl/mainline/service.cpp b/wpa_supplicant/aidl/mainline/service.cpp
index da343ea..002abbb 100644
--- a/wpa_supplicant/aidl/mainline/service.cpp
+++ b/wpa_supplicant/aidl/mainline/service.cpp
@@ -9,6 +9,7 @@
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
 
+#include "callback_manager.h"
 #include "mainline_supplicant.h"
 
 extern "C"
@@ -80,6 +81,7 @@
     }
 
     wpa_printf(MSG_INFO, "AIDL setup is complete");
+    CallbackManager::initialize(global);
     return priv;
 }
 
diff --git a/wpa_supplicant/aidl/mainline/sta_iface.cpp b/wpa_supplicant/aidl/mainline/sta_iface.cpp
new file mode 100644
index 0000000..0eaa199
--- /dev/null
+++ b/wpa_supplicant/aidl/mainline/sta_iface.cpp
@@ -0,0 +1,188 @@
+/*
+ * WPA Supplicant - Station mode interface
+ * Copyright (c) 2024, Google Inc. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "callback_manager.h"
+#include "sta_iface.h"
+#include "usd_utils.h"
+
+extern "C"
+{
+#include "utils/common.h"
+#include "nan_usd.h"
+#include "wpa_supplicant_i.h"
+}
+
+StaIface::StaIface(struct wpa_global* wpa_global, std::string iface_name)
+    : wpa_global_(wpa_global), iface_name_(iface_name) {}
+
+struct wpa_supplicant* StaIface::retrieveIfacePtr() {
+    return wpa_supplicant_get_iface(wpa_global_, iface_name_.c_str());
+}
+
+::ndk::ScopedAStatus StaIface::registerCallback(
+        const std::shared_ptr<IStaInterfaceCallback>& callback) {
+    CallbackManager* callbackManager = CallbackManager::getInstance();
+    WPA_ASSERT(callbackManager);
+    if (callbackManager->registerStaIfaceCallback(iface_name_, callback)) {
+        return ndk::ScopedAStatus::ok();
+    } else {
+        return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+    }
+}
+
+::ndk::ScopedAStatus StaIface::getUsdCapabilities(UsdCapabilities* _aidl_return) {
+    UsdCapabilities capabilities;
+    capabilities.isUsdPublisherSupported = kIsUsdPublisherSupported;
+    capabilities.isUsdSubscriberSupported = kIsUsdSubscriberSupported;
+    capabilities.maxLocalSsiLengthBytes = kMaxUsdLocalSsiLengthBytes;
+    capabilities.maxServiceNameLengthBytes = kMaxUsdServiceNameLengthBytes;
+    capabilities.maxMatchFilterLengthBytes = kMaxUsdMatchFilterLengthBytes;
+    capabilities.maxNumPublishSessions = kMaxNumUsdPublishSessions;
+    capabilities.maxNumSubscribeSessions = kMaxNumUsdSubscribeSessions;
+    *_aidl_return = capabilities;
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus StaIface::startUsdPublish(int32_t cmdId,
+        const UsdPublishConfig& publishConfig) {
+    if (!validateUsdPublishConfig(publishConfig)) {
+        wpa_printf(MSG_ERROR, "USD publish config is invalid");
+        return createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID);
+    }
+    wpabuf_unique_ptr ssiBuffer = {nullptr, nullptr};
+    if (!publishConfig.baseConfig.serviceSpecificInfo.empty()) {
+        ssiBuffer = convertVectorToWpaBuf(publishConfig.baseConfig.serviceSpecificInfo);
+        if (ssiBuffer.get() == nullptr) {
+            wpa_printf(MSG_ERROR, "Unable to convert USD publish SSI to buffer");
+            return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+        }
+    }
+
+    struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+    struct nan_publish_params nanPublishParams =
+        convertAidlUsdPublishConfigToInternal(publishConfig);
+    int publishId = wpas_nan_usd_publish(
+        wpa_s, publishConfig.baseConfig.serviceName.c_str(),
+        convertAidlServiceProtoTypeToInternal(
+            publishConfig.baseConfig.serviceProtoType),
+        ssiBuffer.get(), &nanPublishParams, false /* p2p */);
+
+    // Core supplicant does not have an internal callback for USD publish,
+    // so invoke the failure callback directly if needed.
+    if (publishId < 0) {
+        wpa_printf(MSG_INFO, "Failed to configure USD publish");
+        auto callback = getStaIfaceCallback(iface_name_);
+        if (callback) {
+            callback->onUsdPublishConfigFailed(
+                cmdId, IStaInterfaceCallback::UsdConfigErrorCode::FAILURE_UNKNOWN);
+        }
+        return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus StaIface::startUsdSubscribe(int32_t cmdId,
+        const UsdSubscribeConfig& subscribeConfig) {
+    if (!validateUsdSubscribeConfig(subscribeConfig)) {
+        wpa_printf(MSG_ERROR, "USD subscribe config is invalid");
+        return createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID);
+    }
+    wpabuf_unique_ptr ssiBuffer = {nullptr, nullptr};
+    if (!subscribeConfig.baseConfig.serviceSpecificInfo.empty()) {
+        ssiBuffer = convertVectorToWpaBuf(subscribeConfig.baseConfig.serviceSpecificInfo);
+        if (ssiBuffer.get() == nullptr) {
+            wpa_printf(MSG_ERROR, "Unable to convert USD subscribe SSI to buffer");
+            return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+        }
+    }
+
+    struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+    struct nan_subscribe_params nanSubscribeParams =
+        convertAidlUsdSubscribeConfigToInternal(subscribeConfig);
+    int subscribeId = wpas_nan_usd_subscribe(
+        wpa_s, subscribeConfig.baseConfig.serviceName.c_str(),
+        convertAidlServiceProtoTypeToInternal(
+            subscribeConfig.baseConfig.serviceProtoType),
+        ssiBuffer.get(), &nanSubscribeParams, false /* p2p */);
+
+    // Core supplicant does not have an internal callback for USD subscribe,
+    // so invoke the failure callback directly if needed.
+    if (subscribeId < 0) {
+        wpa_printf(MSG_INFO, "Failed to configure USD subscribe");
+        auto callback = getStaIfaceCallback(iface_name_);
+        if (callback) {
+            callback->onUsdSubscribeConfigFailed(
+                cmdId, IStaInterfaceCallback::UsdConfigErrorCode::FAILURE_UNKNOWN);
+        }
+        return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus StaIface::updateUsdPublish(int32_t publishId,
+        const std::vector<uint8_t>& serviceSpecificInfo) {
+    if (!checkContainerSize(serviceSpecificInfo, kMaxUsdLocalSsiLengthBytes)) {
+        wpa_printf(MSG_ERROR, "Updated USD publish SSI of size %zu exceeds the"
+            " supported size of %d", serviceSpecificInfo.size(),
+            kMaxUsdLocalSsiLengthBytes);
+        return createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID);
+    }
+    wpabuf_unique_ptr ssiBuffer = {nullptr, nullptr};
+    if (!serviceSpecificInfo.empty()) {
+        ssiBuffer = convertVectorToWpaBuf(serviceSpecificInfo);
+        if (ssiBuffer.get() == nullptr) {
+            wpa_printf(MSG_ERROR, "Unable to convert updated USD publish SSI to buffer");
+            return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+        }
+    }
+    int status = wpas_nan_usd_update_publish(
+        retrieveIfacePtr(), publishId, ssiBuffer.get());
+    if (status < 0) {
+        wpa_printf(MSG_ERROR, "Failed to update USD publish");
+        return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus StaIface::cancelUsdPublish(int32_t publishId) {
+    // Status code is returned by the callback
+    wpas_nan_usd_cancel_publish(retrieveIfacePtr(), publishId);
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus StaIface::cancelUsdSubscribe(int32_t subscribeId) {
+    // Status code is returned by the callback
+    wpas_nan_usd_cancel_subscribe(retrieveIfacePtr(), subscribeId);
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus StaIface::sendUsdMessage(const UsdMessageInfo& messageInfo) {
+    if (!checkContainerSize(messageInfo.message, kMaxUsdLocalSsiLengthBytes)) {
+        wpa_printf(MSG_ERROR, "USD message of size %zu exceeds the supported size of %d",
+            messageInfo.message.size(), kMaxUsdLocalSsiLengthBytes);
+        return createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID);
+    }
+    wpabuf_unique_ptr msgBuffer = {nullptr, nullptr};
+    if (!messageInfo.message.empty()) {
+        msgBuffer = convertVectorToWpaBuf(messageInfo.message);
+        if (msgBuffer.get() == nullptr) {
+            wpa_printf(MSG_ERROR, "Unable to convert USD message contents to buffer");
+            return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+        }
+    }
+    int handle = messageInfo.ownId;
+    int reqInstanceId = messageInfo.peerId;
+    int status = wpas_nan_usd_transmit(
+        retrieveIfacePtr(), handle, msgBuffer.get(), nullptr /* elems */,
+        messageInfo.peerMacAddress.data(), reqInstanceId);
+    if (status < 0) {
+        wpa_printf(MSG_ERROR, "Failed to send USD message");
+        return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+    }
+    return ndk::ScopedAStatus::ok();
+}
diff --git a/wpa_supplicant/aidl/mainline/sta_iface.h b/wpa_supplicant/aidl/mainline/sta_iface.h
new file mode 100644
index 0000000..8f9b665
--- /dev/null
+++ b/wpa_supplicant/aidl/mainline/sta_iface.h
@@ -0,0 +1,42 @@
+/*
+ * WPA Supplicant - Station mode interface
+ * Copyright (c) 2024, Google Inc. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MAINLINE_SUPPLICANT_STA_IFACE_H
+#define MAINLINE_SUPPLICANT_STA_IFACE_H
+
+#include <aidl/android/system/wifi/mainline_supplicant/BnStaInterface.h>
+#include <aidl/android/system/wifi/mainline_supplicant/IStaInterfaceCallback.h>
+
+using ::aidl::android::system::wifi::mainline_supplicant::BnStaInterface;
+using ::aidl::android::system::wifi::mainline_supplicant::IStaInterfaceCallback;
+using ::aidl::android::system::wifi::mainline_supplicant::UsdMessageInfo;
+
+class StaIface : public BnStaInterface {
+    public:
+        StaIface(struct wpa_global* wpa_global, std::string iface_name);
+        ::ndk::ScopedAStatus registerCallback(
+            const std::shared_ptr<IStaInterfaceCallback>& in_callback) override;
+        ::ndk::ScopedAStatus getUsdCapabilities(UsdCapabilities* _aidl_return) override;
+        ::ndk::ScopedAStatus startUsdPublish(int32_t in_cmdId,
+            const UsdPublishConfig& in_publishConfig) override;
+        ::ndk::ScopedAStatus startUsdSubscribe(int32_t in_cmdId,
+            const UsdSubscribeConfig& in_subscribeConfig) override;
+        ::ndk::ScopedAStatus updateUsdPublish(int32_t in_publishId,
+            const std::vector<uint8_t>& in_serviceSpecificInfo) override;
+        ::ndk::ScopedAStatus cancelUsdPublish(int32_t in_publishId) override;
+        ::ndk::ScopedAStatus cancelUsdSubscribe(int32_t in_subscribeId) override;
+        ::ndk::ScopedAStatus sendUsdMessage(const UsdMessageInfo& in_messageInfo) override;
+
+    private:
+        wpa_global* wpa_global_;
+        std::string iface_name_;
+
+        struct wpa_supplicant* retrieveIfacePtr();
+};
+
+#endif // MAINLINE_SUPPLICANT_STA_IFACE_H
diff --git a/wpa_supplicant/aidl/mainline/usd_iface.cpp b/wpa_supplicant/aidl/mainline/usd_iface.cpp
deleted file mode 100644
index b514f77..0000000
--- a/wpa_supplicant/aidl/mainline/usd_iface.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * WPA Supplicant - Interface for USD operations
- * Copyright (c) 2024, Google Inc. All rights reserved.
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "usd_iface.h"
-
-UsdIface::UsdIface(struct wpa_global* wpa_global, std::string iface_name)
-    : wpa_global_(wpa_global), iface_name_(iface_name) {}
-
-::ndk::ScopedAStatus UsdIface::getUsdCapabilities(UsdCapabilities* _aidl_return) {
-    return ndk::ScopedAStatus::ok();
-}
-
-::ndk::ScopedAStatus UsdIface::startUsdPublish(int32_t in_cmdId,
-        const PublishConfig& in_usdPublishConfig) {
-    return ndk::ScopedAStatus::ok();
-}
-
-::ndk::ScopedAStatus UsdIface::startUsdSubscribe(int32_t in_cmdId,
-        const SubscribeConfig& in_usdSubscribeConfig) {
-    return ndk::ScopedAStatus::ok();
-}
-
-::ndk::ScopedAStatus UsdIface::updateUsdPublish(int32_t in_publishId,
-        const std::vector<uint8_t>& in_serviceSpecificInfo) {
-    return ndk::ScopedAStatus::ok();
-}
-
-::ndk::ScopedAStatus UsdIface::cancelUsdPublish(int32_t in_publishId) {
-    return ndk::ScopedAStatus::ok();
-}
-
-::ndk::ScopedAStatus UsdIface::cancelUsdSubscribe(int32_t in_subscribeId) {
-    return ndk::ScopedAStatus::ok();
-}
-
-::ndk::ScopedAStatus UsdIface::sendUsdMessage(const UsdMessageInfo& in_messageInfo) {
-    return ndk::ScopedAStatus::ok();
-}
diff --git a/wpa_supplicant/aidl/mainline/usd_iface.h b/wpa_supplicant/aidl/mainline/usd_iface.h
deleted file mode 100644
index 32b86cc..0000000
--- a/wpa_supplicant/aidl/mainline/usd_iface.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * WPA Supplicant - Interface for USD operations
- * Copyright (c) 2024, Google Inc. All rights reserved.
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#ifndef MAINLINE_SUPPLICANT_USD_IFACE_H
-#define MAINLINE_SUPPLICANT_USD_IFACE_H
-
-#include <aidl/android/system/wifi/mainline_supplicant/BnUsdInterface.h>
-
-using ::aidl::android::system::wifi::mainline_supplicant::BnUsdInterface;
-using ::aidl::android::system::wifi::mainline_supplicant::UsdMessageInfo;
-
-class UsdIface : public BnUsdInterface {
-    public:
-        UsdIface(struct wpa_global* wpa_global, std::string iface_name);
-        ::ndk::ScopedAStatus getUsdCapabilities(UsdCapabilities* _aidl_return) override;
-        ::ndk::ScopedAStatus startUsdPublish(int32_t in_cmdId,
-            const PublishConfig& in_usdPublishConfig) override;
-        ::ndk::ScopedAStatus startUsdSubscribe(int32_t in_cmdId,
-            const SubscribeConfig& in_usdSubscribeConfig) override;
-        ::ndk::ScopedAStatus updateUsdPublish(int32_t in_publishId,
-            const std::vector<uint8_t>& in_serviceSpecificInfo) override;
-        ::ndk::ScopedAStatus cancelUsdPublish(int32_t in_publishId) override;
-        ::ndk::ScopedAStatus cancelUsdSubscribe(int32_t in_subscribeId) override;
-        ::ndk::ScopedAStatus sendUsdMessage(const UsdMessageInfo& in_messageInfo) override;
-
-    private:
-        wpa_global* wpa_global_;
-        std::string iface_name_;
-};
-
-#endif // MAINLINE_SUPPLICANT_USD_IFACE_H
diff --git a/wpa_supplicant/aidl/mainline/usd_utils.h b/wpa_supplicant/aidl/mainline/usd_utils.h
new file mode 100644
index 0000000..4d8a52f
--- /dev/null
+++ b/wpa_supplicant/aidl/mainline/usd_utils.h
@@ -0,0 +1,196 @@
+/*
+ * WPA Supplicant - Helper functions for USD
+ * Copyright (c) 2025, Google Inc. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MAINLINE_SUPPLICANT_USD_UTILS_H
+#define MAINLINE_SUPPLICANT_USD_UTILS_H
+
+#include "utils.h"
+
+#include <aidl/android/system/wifi/mainline_supplicant/IStaInterface.h>
+#include <aidl/android/system/wifi/mainline_supplicant/UsdServiceProtoType.h>
+
+extern "C"
+{
+#include "utils/common.h"
+#include "src/common/nan_de.h"
+}
+
+using ::aidl::android::system::wifi::mainline_supplicant::IStaInterface;
+using ::aidl::android::system::wifi::mainline_supplicant::UsdServiceProtoType;
+
+constexpr bool kIsUsdPublisherSupported = true;
+constexpr bool kIsUsdSubscriberSupported = true;
+constexpr int32_t kMaxUsdLocalSsiLengthBytes = 1400;
+constexpr int32_t kMaxUsdServiceNameLengthBytes = 255;
+constexpr int32_t kMaxUsdMatchFilterLengthBytes = 0;
+constexpr int32_t kMaxNumUsdPublishSessions = NAN_DE_MAX_SERVICE;
+constexpr int32_t kMaxNumUsdSubscribeSessions = NAN_DE_MAX_SERVICE;
+
+static bool validateUsdBaseConfig(IStaInterface::UsdBaseConfig baseConfig) {
+    if (!isValidEnumValue(baseConfig.serviceProtoType,
+            UsdServiceProtoType::GENERIC, UsdServiceProtoType::CSA_MATTER)) {
+        wpa_printf(MSG_ERROR, "Unknown protocol type received: %d",
+            static_cast<int>(baseConfig.serviceProtoType));
+        return false;
+    }
+    if (!checkContainerSize(baseConfig.serviceName, kMaxUsdServiceNameLengthBytes)) {
+        wpa_printf(MSG_ERROR, "Service name of size %zu exceeds the supported size of %d",
+            baseConfig.serviceName.size(), kMaxUsdServiceNameLengthBytes);
+        return false;
+    }
+    if (!checkContainerSize(baseConfig.serviceSpecificInfo, kMaxUsdLocalSsiLengthBytes)) {
+        wpa_printf(MSG_ERROR, "Service specific info of size %zu exceeds"
+            " the supported size of %d", baseConfig.serviceSpecificInfo.size(),
+            kMaxUsdLocalSsiLengthBytes);
+        return false;
+    }
+    if (baseConfig.txMatchFilter.has_value() && !checkContainerSize(
+            baseConfig.txMatchFilter.value(), kMaxUsdMatchFilterLengthBytes)) {
+        wpa_printf(MSG_ERROR, "TX match filter of size %zu exceeds"
+            " the supported size of %d", baseConfig.txMatchFilter.value().size(),
+            kMaxUsdMatchFilterLengthBytes);
+        return false;
+    }
+    if (baseConfig.rxMatchFilter.has_value() && !checkContainerSize(
+            baseConfig.rxMatchFilter.value(), kMaxUsdMatchFilterLengthBytes)) {
+        wpa_printf(MSG_ERROR, "RX match filter of size %zu exceeds"
+            " the supported size of %d", baseConfig.rxMatchFilter.value().size(),
+            kMaxUsdMatchFilterLengthBytes);
+        return false;
+    }
+    return true;
+}
+
+static bool validateUsdPublishConfig(IStaInterface::UsdPublishConfig publishConfig) {
+    if (!validateUsdBaseConfig(publishConfig.baseConfig)) {
+        return false;
+    }
+    if (!isValidEnumValue(publishConfig.publishType,
+            IStaInterface::UsdPublishType::SOLICITED_ONLY,
+            IStaInterface::UsdPublishType::SOLICITED_AND_UNSOLICITED)) {
+        wpa_printf(MSG_ERROR, "Unknown publish type received: %d",
+            static_cast<int>(publishConfig.publishType));
+        return false;
+    }
+    if (!isValidEnumValue(publishConfig.transmissionType,
+            IStaInterface::UsdPublishTransmissionType::UNICAST,
+            IStaInterface::UsdPublishTransmissionType::MULTICAST)) {
+        wpa_printf(MSG_ERROR, "Unknown transmission type received: %d",
+            static_cast<int>(publishConfig.transmissionType));
+        return false;
+    }
+    return true;
+}
+
+static bool validateUsdSubscribeConfig(IStaInterface::UsdSubscribeConfig subscribeConfig) {
+    if (!validateUsdBaseConfig(subscribeConfig.baseConfig)) {
+        return false;
+    }
+    if (!isValidEnumValue(subscribeConfig.subscribeType,
+            IStaInterface::UsdSubscribeType::PASSIVE_MODE,
+            IStaInterface::UsdSubscribeType::ACTIVE_MODE)) {
+        wpa_printf(MSG_ERROR, "Unknown subscribe type received: %d",
+            static_cast<int>(subscribeConfig.subscribeType));
+        return false;
+    }
+    return true;
+}
+
+static struct nan_publish_params convertAidlUsdPublishConfigToInternal(
+        IStaInterface::UsdPublishConfig publishConfig) {
+    struct nan_publish_params nanPublishParams;
+    nanPublishParams.unsolicited =
+        publishConfig.publishType == IStaInterface::UsdPublishType::SOLICITED_AND_UNSOLICITED
+            || publishConfig.publishType == IStaInterface::UsdPublishType::UNSOLICITED_ONLY;
+    nanPublishParams.solicited =
+        publishConfig.publishType == IStaInterface::UsdPublishType::SOLICITED_AND_UNSOLICITED
+            || publishConfig.publishType == IStaInterface::UsdPublishType::SOLICITED_ONLY;
+    nanPublishParams.solicited_multicast = nanPublishParams.solicited &&
+        publishConfig.transmissionType == IStaInterface::UsdPublishTransmissionType::MULTICAST;
+    nanPublishParams.ttl = publishConfig.baseConfig.ttlSec;
+    nanPublishParams.fsd = publishConfig.isFsd;
+    nanPublishParams.freq = publishConfig.baseConfig.defaultFreqMhz;
+    nanPublishParams.announcement_period = publishConfig.announcementPeriodMillis;
+    nanPublishParams.disable_events = !publishConfig.eventsEnabled;
+    // Pass the original pointer to the freq list, since the receiver will memcpy the data
+    nanPublishParams.freq_list = publishConfig.baseConfig.freqsMhz.empty()
+        ? NULL : publishConfig.baseConfig.freqsMhz.data();
+    return nanPublishParams;
+}
+
+static struct nan_subscribe_params convertAidlUsdSubscribeConfigToInternal(
+        IStaInterface::UsdSubscribeConfig subscribeConfig) {
+    struct nan_subscribe_params nanSubscribeParams;
+    nanSubscribeParams.active =
+        subscribeConfig.subscribeType == IStaInterface::UsdSubscribeType::ACTIVE_MODE;
+    nanSubscribeParams.ttl = subscribeConfig.baseConfig.ttlSec;
+    nanSubscribeParams.freq = subscribeConfig.baseConfig.defaultFreqMhz;
+    nanSubscribeParams.query_period = subscribeConfig.queryPeriodMillis;
+    // Pass the original pointer to the freq list, since the receiver will memcpy the data
+    nanSubscribeParams.freq_list = subscribeConfig.baseConfig.freqsMhz.empty()
+        ? NULL : subscribeConfig.baseConfig.freqsMhz.data();
+    return nanSubscribeParams;
+}
+
+static nan_service_protocol_type convertAidlServiceProtoTypeToInternal(
+        UsdServiceProtoType serviceProtoType) {
+    switch (serviceProtoType) {
+        case UsdServiceProtoType::GENERIC:
+            return NAN_SRV_PROTO_GENERIC;
+        case UsdServiceProtoType::CSA_MATTER:
+            return NAN_SRV_PROTO_CSA_MATTER;
+        default:
+            // Default case is not expected, due to the USD validation method
+            return NAN_SRV_PROTO_GENERIC;
+    };
+}
+
+static UsdServiceProtoType convertInternalUsdServiceProtoTypeToAidl(
+        nan_service_protocol_type protocolType) {
+    switch (protocolType) {
+        case NAN_SRV_PROTO_GENERIC:
+            return UsdServiceProtoType::GENERIC;
+        case NAN_SRV_PROTO_CSA_MATTER:
+            return UsdServiceProtoType::CSA_MATTER;
+        default:
+            wpa_printf(MSG_ERROR, "Received invalid USD proto type %d from internal",
+                static_cast<int>(protocolType));
+            return UsdServiceProtoType::GENERIC;
+    }
+}
+
+static IStaInterfaceCallback::UsdServiceDiscoveryInfo createUsdServiceDiscoveryInfo(
+        enum nan_service_protocol_type srv_proto_type,
+        int own_id, int peer_id, const u8 *peer_addr,
+        bool fsd, const u8 *ssi, size_t ssi_len) {
+    IStaInterfaceCallback::UsdServiceDiscoveryInfo discoveryInfo;
+    discoveryInfo.ownId = own_id;
+    discoveryInfo.peerId = peer_id;
+    // TODO: Fill the matchFilter field in the AIDL struct
+    discoveryInfo.matchFilter = std::vector<uint8_t>();
+    discoveryInfo.peerMacAddress = macAddrBytesToArray(peer_addr);
+    discoveryInfo.serviceProtoType = convertInternalUsdServiceProtoTypeToAidl(srv_proto_type);
+    discoveryInfo.serviceSpecificInfo = ssi ? byteArrToVec(ssi, ssi_len) : std::vector<uint8_t>();
+    discoveryInfo.isFsd = fsd;
+    return discoveryInfo;
+}
+
+static IStaInterfaceCallback::UsdTerminateReasonCode convertInternalUsdTerminateReasonCodeToAidl(
+        nan_de_reason terminateReason) {
+    switch (terminateReason) {
+        case NAN_DE_REASON_TIMEOUT:
+            return IStaInterfaceCallback::UsdTerminateReasonCode::TIMEOUT;
+        case NAN_DE_REASON_USER_REQUEST:
+            return IStaInterfaceCallback::UsdTerminateReasonCode::USER_REQUESTED;
+        case NAN_DE_REASON_FAILURE:
+        default:
+            return IStaInterfaceCallback::UsdTerminateReasonCode::FAILURE_UNKNOWN;
+    }
+}
+
+#endif // MAINLINE_SUPPLICANT_USD_UTILS_H
diff --git a/wpa_supplicant/aidl/mainline/utils.h b/wpa_supplicant/aidl/mainline/utils.h
index 703b9ee..bf7b9fb 100644
--- a/wpa_supplicant/aidl/mainline/utils.h
+++ b/wpa_supplicant/aidl/mainline/utils.h
@@ -9,17 +9,79 @@
 #ifndef MAINLINE_SUPPLICANT_UTILS_H
 #define MAINLINE_SUPPLICANT_UTILS_H
 
+#include "callback_manager.h"
+
 #include <aidl/android/system/wifi/mainline_supplicant/SupplicantStatusCode.h>
 
+extern "C"
+{
+#include "utils/common.h"
+#include "wpabuf.h"
+}
+
+namespace {
+// Custom deleter for wpabuf
+void freeWpaBuf(wpabuf *ptr) { wpabuf_free(ptr); }
+}
+
+using ::aidl::android::system::wifi::mainline_supplicant::SupplicantStatusCode;
+
+using wpabuf_unique_ptr = std::unique_ptr<wpabuf, void (*)(wpabuf *)>;
+
 inline ndk::ScopedAStatus createStatus(SupplicantStatusCode statusCode) {
-	return ndk::ScopedAStatus::fromServiceSpecificError(
-		static_cast<int32_t>(statusCode));
+    return ndk::ScopedAStatus::fromServiceSpecificError(
+        static_cast<int32_t>(statusCode));
 }
 
 inline ndk::ScopedAStatus createStatusWithMsg(
-	    SupplicantStatusCode statusCode, std::string msg) {
-	return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
-		static_cast<int32_t>(statusCode), msg.c_str());
+        SupplicantStatusCode statusCode, std::string msg) {
+    return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+        static_cast<int32_t>(statusCode), msg.c_str());
+}
+
+// Check whether the container is within the maximum size
+template <typename T>
+inline bool checkContainerSize(const T& container, int maxSize) {
+    return container.size() <= maxSize;
+}
+
+// Check whether the enum value is within the specified range
+template <typename T>
+inline bool isValidEnumValue(T value, T enumRangeMin, T enumRangeMax) {
+    return static_cast<uint32_t>(value) >= static_cast<uint32_t>(enumRangeMin)
+        && static_cast<uint32_t>(value) <= static_cast<uint32_t>(enumRangeMax);
+}
+
+// Create a unique_ptr for a wpabuf ptr with a custom deleter
+inline wpabuf_unique_ptr createWpaBufUniquePtr(struct wpabuf *raw_ptr) {
+    return {raw_ptr, freeWpaBuf};
+}
+
+// Create a wpabuf ptr with a custom deleter, copying the data from the provided vector
+inline wpabuf_unique_ptr convertVectorToWpaBuf(const std::vector<uint8_t> &data) {
+    return createWpaBufUniquePtr(wpabuf_alloc_copy(data.data(), data.size()));
+}
+
+// Convert a byte array representation of a MAC address to an std::array
+inline std::array<uint8_t, ETH_ALEN> macAddrBytesToArray(const uint8_t* mac_addr) {
+    std::array<uint8_t, ETH_ALEN> arr;
+    std::copy(mac_addr, mac_addr + ETH_ALEN, std::begin(arr));
+    return arr;
+}
+
+// Convert a byte array to an std::vector
+inline std::vector<uint8_t> byteArrToVec(const uint8_t* arr, int len) {
+    return std::vector<uint8_t>{arr, arr + len};
+}
+
+// Wrapper to retrieve a STA iface callback registered with the callback manager
+static std::shared_ptr<IStaInterfaceCallback> getStaIfaceCallback(
+        std::string ifaceName) {
+    CallbackManager* callback_manager = CallbackManager::getInstance();
+    if (!callback_manager) {
+        return nullptr;
+    }
+    return callback_manager->getStaIfaceCallback(ifaceName);
 }
 
 #endif // MAINLINE_SUPPLICANT_UTILS_H
diff --git a/wpa_supplicant/aidl/vendor/aidl_manager.cpp b/wpa_supplicant/aidl/vendor/aidl_manager.cpp
index 60ce800..24e5a56 100644
--- a/wpa_supplicant/aidl/vendor/aidl_manager.cpp
+++ b/wpa_supplicant/aidl/vendor/aidl_manager.cpp
@@ -1866,7 +1866,11 @@
 		return;
 	}
 
-	aidl_dpp_config_data.password = misc_utils::charBufToString(config->passphrase);
+	if (aidl_dpp_config_data.securityAkm == DppAkm::SAE)
+		aidl_dpp_config_data.password = misc_utils::charBufToString(config->sae_password);
+	else
+		aidl_dpp_config_data.password = misc_utils::charBufToString(config->passphrase);
+
 	aidl_dpp_config_data.psk = byteArrToVec(config->psk, 32);
 	std::vector<uint8_t> aidl_ssid(
 		config->ssid,
diff --git a/wpa_supplicant/aidl/vendor/sta_iface.cpp b/wpa_supplicant/aidl/vendor/sta_iface.cpp
index fe6738c..f6868d0 100644
--- a/wpa_supplicant/aidl/vendor/sta_iface.cpp
+++ b/wpa_supplicant/aidl/vendor/sta_iface.cpp
@@ -23,6 +23,7 @@
 #include "dpp_supplicant.h"
 #include "rsn_supp/wpa.h"
 #include "rsn_supp/pmksa_cache.h"
+#include "src/common/nan_de.h"
 }
 
 namespace {
@@ -64,13 +65,23 @@
 	static_cast<uint32_t>(ISupplicant::EXT_RADIO_WORK_TIMEOUT_IN_SECS);
 constexpr char kExtRadioWorkNamePrefix[] = "ext:";
 
-constexpr bool kIsUsdPublisherSupported = false;
-constexpr bool kIsUsdSubscriberSupported = false;
+#ifdef CONFIG_NAN_USD
+constexpr bool kIsUsdPublisherSupported = true;
+constexpr bool kIsUsdSubscriberSupported = true;
 constexpr int32_t kMaxUsdLocalSsiLengthBytes = 1400;
 constexpr int32_t kMaxUsdServiceNameLengthBytes = 255;
-constexpr int32_t kMaxUsdMatchFilterLengthBytes = 255;
-constexpr int32_t kMaxNumUsdPublishSessions = 1;
-constexpr int32_t kMaxNumUsdSubscribeSessions = 1;
+constexpr int32_t kMaxUsdMatchFilterLengthBytes = 0;
+constexpr int32_t kMaxNumUsdPublishSessions = NAN_DE_MAX_SERVICE;
+constexpr int32_t kMaxNumUsdSubscribeSessions = NAN_DE_MAX_SERVICE;
+#else
+constexpr bool kIsUsdPublisherSupported = false;
+constexpr bool kIsUsdSubscriberSupported = false;
+constexpr int32_t kMaxUsdLocalSsiLengthBytes = 0;
+constexpr int32_t kMaxUsdServiceNameLengthBytes = 0;
+constexpr int32_t kMaxUsdMatchFilterLengthBytes = 0;
+constexpr int32_t kMaxNumUsdPublishSessions = 0;
+constexpr int32_t kMaxNumUsdSubscribeSessions = 0;
+#endif
 
 uint8_t convertAidlRxFilterTypeToInternal(
 	RxFilterType type)
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index d5a34c5..ea6fc69 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -27,6 +27,7 @@
 #include "sme.h"
 #include "notify.h"
 #include "aidl/vendor/aidl.h"
+#include "aidl/mainline/callback_bridge.h"
 
 #ifdef MAINLINE_SUPPLICANT
 #include "aidl/mainline/service.h"
@@ -1533,6 +1534,8 @@
 
 	wpas_aidl_notify_usd_service_discovered(wpa_s, srv_proto_type,
 		subscribe_id, peer_publish_id, peer_addr, fsd, ssi, ssi_len);
+	mainline_aidl_notify_usd_service_discovered(wpa_s, srv_proto_type,
+		subscribe_id, peer_publish_id, peer_addr, fsd, ssi, ssi_len);
 
 	wpas_dbus_signal_nan_discovery_result(wpa_s, srv_proto_type,
 					      subscribe_id, peer_publish_id,
@@ -1563,6 +1566,8 @@
 
 	wpas_aidl_notify_usd_publish_replied(wpa_s, srv_proto_type,
 		publish_id, peer_subscribe_id, peer_addr, ssi, ssi_len);
+	mainline_aidl_notify_usd_publish_replied(wpa_s, srv_proto_type,
+		publish_id, peer_subscribe_id, peer_addr, ssi, ssi_len);
 
 	wpas_dbus_signal_nan_replied(wpa_s, srv_proto_type, publish_id,
 				     peer_subscribe_id, peer_addr,
@@ -1588,6 +1593,8 @@
 
 	wpas_aidl_notify_usd_message_received(wpa_s, id, peer_instance_id,
 		peer_addr, ssi, ssi_len);
+	mainline_aidl_notify_usd_message_received(wpa_s, id, peer_instance_id,
+		peer_addr, ssi, ssi_len);
 
 	wpas_dbus_signal_nan_receive(wpa_s, id, peer_instance_id, peer_addr,
 				     ssi, ssi_len);
@@ -1616,8 +1623,9 @@
 	wpa_msg_global(wpa_s, MSG_INFO, NAN_PUBLISH_TERMINATED
 		       "publish_id=%d reason=%s",
 		       publish_id, nan_reason_txt(reason));
-        
+
 	wpas_aidl_notify_usd_publish_terminated(wpa_s, publish_id, reason);
+	mainline_aidl_notify_usd_publish_terminated(wpa_s, publish_id, reason);
 
 	wpas_dbus_signal_nan_publish_terminated(wpa_s, publish_id,
 						nan_reason_txt(reason));
@@ -1633,6 +1641,7 @@
 		       subscribe_id, nan_reason_txt(reason));
 
 	wpas_aidl_notify_usd_subscribe_terminated(wpa_s, subscribe_id, reason);
+	mainline_aidl_notify_usd_subscribe_terminated(wpa_s, subscribe_id, reason);
 
 	wpas_dbus_signal_nan_subscribe_terminated(wpa_s, subscribe_id,
 						  nan_reason_txt(reason));