Add callback manager class to manage all callbacks
registered with the mainline supplicant.

This is largely equivalent to the aidl_manager
class in the vendor supplicant.

Bug: 365585450
Test: m
Change-Id: I35dee68e1e65bf827a219719f34c4af89a5d3d46
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 b1a873d..a2dc8b7 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"
 
@@ -80,8 +81,13 @@
         return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
     }
 
-    wpa_printf(MSG_INFO, "Interface %s was removed successfully", ifaceName.c_str());
+    // 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());
     return ndk::ScopedAStatus::ok();
 }
 
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
index 08d2b04..90543e6 100644
--- a/wpa_supplicant/aidl/mainline/sta_iface.cpp
+++ b/wpa_supplicant/aidl/mainline/sta_iface.cpp
@@ -6,6 +6,7 @@
  * See README for more details.
  */
 
+#include "callback_manager.h"
 #include "sta_iface.h"
 #include "usd_utils.h"
 
@@ -24,8 +25,14 @@
 }
 
 ::ndk::ScopedAStatus StaIface::registerCallback(
-        const std::shared_ptr<IStaInterfaceCallback>& in_callback) {
-    return ndk::ScopedAStatus::ok();
+        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) {