binder: Implement network addition/removal

Implement the |IIface.AddNetwork| & |IIface.RemoveNetwork| binder calls.
Also hookup the network addition notifications to BinderManager for
creating corresponding network binder objects. The network binder
objects are keyed using |ifname|_|network_id|.

BUG: 30015382
Change-Id: I0842563e74ff8b120d34f63fa28965bf264bb55f
TEST: Ran the integration tests under |wificond|.
Signed-off-by: Roshan Pius <rpius@google.com>
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 5ca994e..1363d84 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -1673,8 +1673,11 @@
 LOCAL_CFLAGS := $(L_CFLAGS)
 LOCAL_C_INCLUDES := $(INCLUDES)
 LOCAL_SRC_FILES := \
-    binder/binder.cpp binder/binder_manager.cpp \
-    binder/supplicant.cpp binder/iface.cpp
+    binder/binder.cpp \
+    binder/binder_manager.cpp \
+    binder/iface.cpp \
+    binder/network.cpp \
+    binder/supplicant.cpp
 LOCAL_SHARED_LIBRARIES := \
     libbinder \
     libutils
diff --git a/wpa_supplicant/binder/binder.cpp b/wpa_supplicant/binder/binder.cpp
index 9fee001..704b1c3 100644
--- a/wpa_supplicant/binder/binder.cpp
+++ b/wpa_supplicant/binder/binder.cpp
@@ -116,3 +116,38 @@
 
 	return binder_manager->unregisterInterface(wpa_s);
 }
+
+int wpas_binder_register_network(
+    struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+	if (!wpa_s->global->binder || !wpa_s || !ssid)
+		return 1;
+
+	wpa_printf(
+	    MSG_DEBUG, "Registering network to binder control: %d", ssid->id);
+
+	wpa_supplicant_binder::BinderManager *binder_manager =
+	    wpa_supplicant_binder::BinderManager::getInstance();
+	if (!binder_manager)
+		return 1;
+
+	return binder_manager->registerNetwork(wpa_s, ssid);
+}
+
+int wpas_binder_unregister_network(
+    struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+	if (!wpa_s->global->binder || !wpa_s || !ssid)
+		return 1;
+
+	wpa_printf(
+	    MSG_DEBUG, "Deregistering network from binder control: %d",
+	    ssid->id);
+
+	wpa_supplicant_binder::BinderManager *binder_manager =
+	    wpa_supplicant_binder::BinderManager::getInstance();
+	if (!binder_manager)
+		return 1;
+
+	return binder_manager->unregisterNetwork(wpa_s, ssid);
+}
diff --git a/wpa_supplicant/binder/binder.h b/wpa_supplicant/binder/binder.h
index 019e327..4ca2c86 100644
--- a/wpa_supplicant/binder/binder.h
+++ b/wpa_supplicant/binder/binder.h
@@ -28,6 +28,10 @@
 #ifdef CONFIG_CTRL_IFACE_BINDER
 int wpas_binder_register_interface(struct wpa_supplicant *wpa_s);
 int wpas_binder_unregister_interface(struct wpa_supplicant *wpa_s);
+int wpas_binder_register_network(
+    struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+int wpas_binder_unregister_network(
+    struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
 #else  /* CONFIG_CTRL_IFACE_BINDER */
 static inline int wpas_binder_register_interface(struct wpa_supplicant *wpa_s)
 {
@@ -37,6 +41,16 @@
 {
 	return 0;
 }
+static inline int wpas_binder_register_network(
+    struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+	return 0;
+}
+static inline int wpas_binder_unregister_network(
+    struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+	return 0;
+}
 #endif /* CONFIG_CTRL_IFACE_BINDER */
 
 #ifdef _cplusplus
diff --git a/wpa_supplicant/binder/binder_manager.cpp b/wpa_supplicant/binder/binder_manager.cpp
index 00fa70f..5152ed9 100644
--- a/wpa_supplicant/binder/binder_manager.cpp
+++ b/wpa_supplicant/binder/binder_manager.cpp
@@ -94,6 +94,63 @@
 }
 
 /**
+ * Register a network to binder manager.
+ *
+ * @param wpa_s |wpa_supplicant| struct corresponding to the interface on which
+ * the network is added.
+ * @param ssid |wpa_ssid| struct corresponding to the network being added.
+ *
+ * @return 0 on success, 1 on failure.
+ */
+int BinderManager::registerNetwork(
+    struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+	if (!wpa_s || !ssid)
+		return 1;
+
+	// Generate the key to be used to lookup the network.
+	const std::string network_key =
+	    getNetworkObjectMapKey(wpa_s->ifname, ssid->id);
+
+	if (network_object_map_.find(network_key) != network_object_map_.end())
+		return 1;
+
+	network_object_map_[network_key] =
+	    new Network(wpa_s->global, wpa_s->ifname, ssid->id);
+	if (!network_object_map_[network_key].get())
+		return 1;
+
+	return 0;
+}
+
+/**
+ * Unregister a network from binder manager.
+ *
+ * @param wpa_s |wpa_supplicant| struct corresponding to the interface on which
+ * the network is added.
+ * @param ssid |wpa_ssid| struct corresponding to the network being added.
+ *
+ * @return 0 on success, 1 on failure.
+ */
+int BinderManager::unregisterNetwork(
+    struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+	if (!wpa_s || !ssid)
+		return 1;
+
+	// Generate the key to be used to lookup the network.
+	const std::string network_key =
+	    getNetworkObjectMapKey(wpa_s->ifname, ssid->id);
+
+	if (network_object_map_.find(network_key) == network_object_map_.end())
+		return 1;
+
+	/* Delete the corresponding network object from our map. */
+	network_object_map_.erase(network_key);
+	return 0;
+}
+
+/**
  * Retrieve the |IIface| binder object reference using the provided ifname.
  *
  * @param ifname Name of the corresponding interface.
@@ -115,4 +172,46 @@
 	return 0;
 }
 
+/**
+ * Retrieve the |INetwork| binder object reference using the provided ifname
+ * and network_id.
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param network_id ID of the corresponding network.
+ * @param network_object Binder reference corresponding to the network.
+ *
+ * @return 0 on success, 1 on failure.
+ */
+int BinderManager::getNetworkBinderObjectByIfnameAndNetworkId(
+    const std::string &ifname, int network_id,
+    android::sp<fi::w1::wpa_supplicant::INetwork> *network_object)
+{
+	if (ifname.empty() || network_id < 0 || !network_object)
+		return 1;
+
+	// Generate the key to be used to lookup the network.
+	const std::string network_key =
+	    getNetworkObjectMapKey(ifname, network_id);
+
+	if (network_object_map_.find(network_key) == network_object_map_.end())
+		return 1;
+
+	*network_object = network_object_map_[network_key];
+	return 0;
+}
+
+/**
+ * Creates a unique key for the network using the provided |ifname| and
+ * |network_id| to be used
+ * in the internal map of |INetwork| objects.
+ * This is of the form |ifname|_|network_id|. For ex: "wlan0_1".
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param network_id ID of the corresponding network.
+ */
+const std::string
+BinderManager::getNetworkObjectMapKey(const std::string &ifname, int network_id)
+{
+	return ifname + "_" + std::to_string(network_id);
+}
 } // namespace wpa_supplicant_binder
diff --git a/wpa_supplicant/binder/binder_manager.h b/wpa_supplicant/binder/binder_manager.h
index b4b8f76..377c94f 100644
--- a/wpa_supplicant/binder/binder_manager.h
+++ b/wpa_supplicant/binder/binder_manager.h
@@ -14,6 +14,7 @@
 #include <string>
 
 #include "iface.h"
+#include "network.h"
 #include "supplicant.h"
 
 struct wpa_global;
@@ -32,12 +33,20 @@
 public:
 	static BinderManager *getInstance();
 	static void destroyInstance();
+
 	int registerBinderService(struct wpa_global *global);
 	int registerInterface(struct wpa_supplicant *wpa_s);
 	int unregisterInterface(struct wpa_supplicant *wpa_s);
+	int
+	registerNetwork(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+	int
+	unregisterNetwork(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
 	int getIfaceBinderObjectByIfname(
 	    const std::string &ifname,
 	    android::sp<fi::w1::wpa_supplicant::IIface> *iface_object);
+	int getNetworkBinderObjectByIfnameAndNetworkId(
+	    const std::string &ifname, int network_id,
+	    android::sp<fi::w1::wpa_supplicant::INetwork> *network_object);
 
 private:
 	BinderManager() = default;
@@ -45,6 +54,9 @@
 	BinderManager(const BinderManager &) = default;
 	BinderManager &operator=(const BinderManager &) = default;
 
+	const std::string
+	getNetworkObjectMapKey(const std::string &ifname, int network_id);
+
 	// Singleton instance of this class.
 	static BinderManager *instance_;
 	// The main binder service object.
@@ -53,6 +65,10 @@
 	// wpa_supplicant. This map is keyed in by the corresponding
 	// |ifname|.
 	std::map<const std::string, android::sp<Iface>> iface_object_map_;
+	// Map of all the network specific binder objects controlled by
+	// wpa_supplicant. This map is keyed in by the corresponding
+	// |ifname| & |network_id|.
+	std::map<const std::string, android::sp<Network>> network_object_map_;
 };
 
 } // namespace wpa_supplicant_binder
diff --git a/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIface.aidl b/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIface.aidl
index dd0fbaa..4e486ea 100644
--- a/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIface.aidl
+++ b/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIface.aidl
@@ -20,6 +20,8 @@
 	const int ERROR_GENERIC = 1;
 	/* Iface is no longer valid */
 	const int ERROR_IFACE_INVALID = 2;
+	/* Network being removed/retrieved does not exist */
+	const int ERROR_NETWORK_UNKNOWN = 3;
 
 	/**
 	 * Retrieves the name of the network interface.
@@ -44,4 +46,16 @@
 	 * @param id Network ID allocated to the corresponding network.
 	 */
 	void RemoveNetwork(in int id);
+
+	/**
+	 * Gets a binder object for the network corresponding to the network_id.
+	 *
+	 * Use |INetwork.GetId()| on the corresponding network binder object
+	 * to retrieve the ID.
+	 *
+	 * @param id Network ID allocated to the corresponding network.
+	 *
+	 * @return Binder object representing the network.
+	 */
+	INetwork GetNetwork(in int id);
 }
diff --git a/wpa_supplicant/binder/iface.cpp b/wpa_supplicant/binder/iface.cpp
index 6a3e223..267b90b 100644
--- a/wpa_supplicant/binder/iface.cpp
+++ b/wpa_supplicant/binder/iface.cpp
@@ -7,6 +7,7 @@
  * See README for more details.
  */
 
+#include "binder_manager.h"
 #include "iface.h"
 
 namespace wpa_supplicant_binder {
@@ -34,11 +35,88 @@
 android::binder::Status Iface::AddNetwork(
     android::sp<fi::w1::wpa_supplicant::INetwork> *network_object_out)
 {
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	if (!wpa_s) {
+		return android::binder::Status::fromServiceSpecificError(
+		    ERROR_IFACE_INVALID,
+		    "wpa_supplicant does not control this interface.");
+	}
+
+	struct wpa_ssid *ssid = wpa_config_add_network(wpa_s->conf);
+	if (!ssid) {
+		return android::binder::Status::fromServiceSpecificError(
+		    ERROR_GENERIC, "wpa_supplicant couldn't add this network.");
+	}
+
+	// This sequence of steps after network addition is following what is
+	// currently being done in |ctrl_iface.c| & |dbus_new_handlers|.
+	// Notify the control interfaces about the network addition.
+	wpas_notify_network_added(wpa_s, ssid);
+	// Set the new network to be disabled.
+	ssid->disabled = 1;
+	// Set defaults for the new network.
+	wpa_config_set_network_defaults(ssid);
+
+	BinderManager *binder_manager = BinderManager::getInstance();
+	if (!binder_manager ||
+	    binder_manager->getNetworkBinderObjectByIfnameAndNetworkId(
+		wpa_s->ifname, ssid->id, network_object_out)) {
+		return android::binder::Status::fromServiceSpecificError(
+		    ERROR_GENERIC,
+		    "wpa_supplicant encountered a binder error.");
+	}
 	return android::binder::Status::ok();
 }
 
 android::binder::Status Iface::RemoveNetwork(int network_id)
 {
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	if (!wpa_s) {
+		return android::binder::Status::fromServiceSpecificError(
+		    ERROR_IFACE_INVALID,
+		    "wpa_supplicant does not control this interface.");
+	}
+
+	struct wpa_ssid *ssid = wpa_config_get_network(wpa_s->conf, network_id);
+	if (!ssid) {
+		return android::binder::Status::fromServiceSpecificError(
+		    ERROR_NETWORK_UNKNOWN,
+		    "wpa_supplicant does not control this network.");
+	}
+	if (wpa_config_remove_network(wpa_s->conf, network_id)) {
+		return android::binder::Status::fromServiceSpecificError(
+		    ERROR_GENERIC,
+		    "wpa_supplicant couldn't remove this network.");
+	}
+	return android::binder::Status::ok();
+}
+
+android::binder::Status Iface::GetNetwork(
+    int network_id,
+    android::sp<fi::w1::wpa_supplicant::INetwork> *network_object_out)
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	if (!wpa_s) {
+		return android::binder::Status::fromServiceSpecificError(
+		    ERROR_IFACE_INVALID,
+		    "wpa_supplicant does not control this interface.");
+	}
+
+	struct wpa_ssid *ssid = wpa_config_get_network(wpa_s->conf, network_id);
+	if (!ssid) {
+		return android::binder::Status::fromServiceSpecificError(
+		    ERROR_NETWORK_UNKNOWN,
+		    "wpa_supplicant does not control this network.");
+	}
+
+	BinderManager *binder_manager = BinderManager::getInstance();
+	if (!binder_manager ||
+	    binder_manager->getNetworkBinderObjectByIfnameAndNetworkId(
+		wpa_s->ifname, ssid->id, network_object_out)) {
+		return android::binder::Status::fromServiceSpecificError(
+		    ERROR_GENERIC,
+		    "wpa_supplicant encountered a binder error.");
+	}
 	return android::binder::Status::ok();
 }
 
diff --git a/wpa_supplicant/binder/iface.h b/wpa_supplicant/binder/iface.h
index bf8c6f0..13dd4b0 100644
--- a/wpa_supplicant/binder/iface.h
+++ b/wpa_supplicant/binder/iface.h
@@ -18,6 +18,8 @@
 extern "C" {
 #include "utils/common.h"
 #include "utils/includes.h"
+#include "../config.h"
+#include "../notify.h"
 #include "../wpa_supplicant_i.h"
 }
 
@@ -40,6 +42,10 @@
 	    android::sp<fi::w1::wpa_supplicant::INetwork> *network_object_out)
 	    override;
 	android::binder::Status RemoveNetwork(int network_id) override;
+	android::binder::Status GetNetwork(
+	    int network_id,
+	    android::sp<fi::w1::wpa_supplicant::INetwork> *network_object_out)
+	    override;
 
 private:
 	struct wpa_supplicant *retrieveIfacePtr();
diff --git a/wpa_supplicant/binder/network.h b/wpa_supplicant/binder/network.h
index b8f3917..dfc1698 100644
--- a/wpa_supplicant/binder/network.h
+++ b/wpa_supplicant/binder/network.h
@@ -17,6 +17,7 @@
 extern "C" {
 #include "utils/common.h"
 #include "utils/includes.h"
+#include "../config.h"
 #include "../wpa_supplicant_i.h"
 }
 
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 536f3e6..b2b4225 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -328,8 +328,10 @@
 	 * applications since these network objects won't behave like
 	 * regular ones.
 	 */
-	if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s)
+	if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s) {
 		wpas_dbus_register_network(wpa_s, ssid);
+		wpas_binder_register_network(wpa_s, ssid);
+	}
 }
 
 
@@ -359,8 +361,10 @@
 	if (wpa_s->wpa)
 		wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
 	if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s &&
-	    !wpa_s->p2p_mgmt)
+	    !wpa_s->p2p_mgmt) {
 		wpas_dbus_unregister_network(wpa_s, ssid->id);
+		wpas_binder_unregister_network(wpa_s, ssid);
+	}
 	if (network_is_persistent_group(ssid))
 		wpas_notify_persistent_group_removed(wpa_s, ssid);