binder: Implement |OnStateChanged| callback

Add state change callbacks. This is used by clients to monitor wpa_supplicant's
connection status, etc.

BUG: 30093041
TEST: Ran a simple connect/disconnect integration test gtest.

Change-Id: I35238f0f95b0a0a4723cb315ddaba30dd19d7dab
Signed-off-by: Roshan Pius <rpius@google.com>
diff --git a/wpa_supplicant/binder/binder.cpp b/wpa_supplicant/binder/binder.cpp
index 704b1c3..d174f2b 100644
--- a/wpa_supplicant/binder/binder.cpp
+++ b/wpa_supplicant/binder/binder.cpp
@@ -85,7 +85,7 @@
 
 int wpas_binder_register_interface(struct wpa_supplicant *wpa_s)
 {
-	if (!wpa_s->global->binder || !wpa_s)
+	if (!wpa_s || !wpa_s->global->binder)
 		return 1;
 
 	wpa_printf(
@@ -102,7 +102,7 @@
 
 int wpas_binder_unregister_interface(struct wpa_supplicant *wpa_s)
 {
-	if (!wpa_s->global->binder || !wpa_s)
+	if (!wpa_s || !wpa_s->global->binder)
 		return 1;
 
 	wpa_printf(
@@ -120,7 +120,7 @@
 int wpas_binder_register_network(
     struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
 {
-	if (!wpa_s->global->binder || !wpa_s || !ssid)
+	if (!wpa_s || !wpa_s->global->binder || !ssid)
 		return 1;
 
 	wpa_printf(
@@ -137,7 +137,7 @@
 int wpas_binder_unregister_network(
     struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
 {
-	if (!wpa_s->global->binder || !wpa_s || !ssid)
+	if (!wpa_s || !wpa_s->global->binder || !ssid)
 		return 1;
 
 	wpa_printf(
@@ -151,3 +151,20 @@
 
 	return binder_manager->unregisterNetwork(wpa_s, ssid);
 }
+
+int wpas_binder_notify_state_changed(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s || !wpa_s->global->binder || !ssid)
+		return 1;
+
+	wpa_printf(
+	    MSG_DEBUG, "Notifying state change event to binder control: %d",
+	    wpa_s->wpa_state);
+
+	wpa_supplicant_binder::BinderManager *binder_manager =
+	    wpa_supplicant_binder::BinderManager::getInstance();
+	if (!binder_manager)
+		return 1;
+
+	return binder_manager->notifyStateChange(wpa_s);
+}
diff --git a/wpa_supplicant/binder/binder.h b/wpa_supplicant/binder/binder.h
index 4ca2c86..7afc6ef 100644
--- a/wpa_supplicant/binder/binder.h
+++ b/wpa_supplicant/binder/binder.h
@@ -32,6 +32,7 @@
     struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
 int wpas_binder_unregister_network(
     struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+int wpas_binder_notify_state_changed(struct wpa_supplicant *wpa_s);
 #else  /* CONFIG_CTRL_IFACE_BINDER */
 static inline int wpas_binder_register_interface(struct wpa_supplicant *wpa_s)
 {
@@ -51,6 +52,10 @@
 {
 	return 0;
 }
+static inline int wpas_binder_notify_state_changed(struct wpa_supplicant *wpa_s)
+{
+	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 d5a502c..ab79f91 100644
--- a/wpa_supplicant/binder/binder_manager.cpp
+++ b/wpa_supplicant/binder/binder_manager.cpp
@@ -216,8 +216,45 @@
 	    wpa_s->ifname,
 	    std::bind(
 		&fi::w1::wpa_supplicant::IIfaceCallback::OnNetworkRemoved,
-		std::placeholders::_1,
-		ssid->id));
+		std::placeholders::_1, ssid->id));
+	return 0;
+}
+
+/**
+ * Notify all listeners about any state changes on a particular interface.
+ *
+ * @param wpa_s |wpa_supplicant| struct corresponding to the interface on which
+ * the state change event occured.
+ *
+ * @return 0 on success, 1 on failure.
+ */
+int BinderManager::notifyStateChange(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s)
+		return 1;
+
+	const std::string ifname(wpa_s->ifname);
+
+	if (iface_object_map_.find(ifname) == iface_object_map_.end())
+		return 1;
+
+	// Invoke the |OnStateChanged| method on all registered callbacks.
+	int state = wpa_s->wpa_state;
+	std::vector<uint8_t> bssid(wpa_s->bssid, wpa_s->bssid + ETH_ALEN);
+	int network_id =
+	    fi::w1::wpa_supplicant::IIfaceCallback::NETWORK_ID_INVALID;
+	std::vector<uint8_t> ssid;
+	if (wpa_s->current_ssid) {
+		network_id = wpa_s->current_ssid->id;
+		ssid.assign(
+		    wpa_s->current_ssid->ssid,
+		    wpa_s->current_ssid->ssid + wpa_s->current_ssid->ssid_len);
+	}
+	callWithEachIfaceCallback(
+	    wpa_s->ifname,
+	    std::bind(
+		&fi::w1::wpa_supplicant::IIfaceCallback::OnStateChanged,
+		std::placeholders::_1, state, bssid, network_id, ssid));
 	return 0;
 }
 
diff --git a/wpa_supplicant/binder/binder_manager.h b/wpa_supplicant/binder/binder_manager.h
index 01ff2fc..200d7e6 100644
--- a/wpa_supplicant/binder/binder_manager.h
+++ b/wpa_supplicant/binder/binder_manager.h
@@ -46,6 +46,7 @@
 	registerNetwork(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
 	int
 	unregisterNetwork(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+	int notifyStateChange(struct wpa_supplicant *wpa_s);
 
 	// Methods called from binder objects.
 	int getIfaceBinderObjectByIfname(
@@ -235,5 +236,12 @@
 	WPA_CIPHER_CCMP,
     "PairwiseCipher value mismatch");
 
+static_assert(
+    WPA_DISCONNECTED ==
+	fi::w1::wpa_supplicant::IIfaceCallback::STATE_DISCONNECTED,
+    "State value mismatch");
+static_assert(
+    WPA_COMPLETED == fi::w1::wpa_supplicant::IIfaceCallback::STATE_COMPLETED,
+    "State value mismatch");
 } // namespace wpa_supplicant_binder
 #endif // WPA_SUPPLICANT_BINDER_BINDER_MANAGER_H
diff --git a/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIfaceCallback.aidl b/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIfaceCallback.aidl
index f740b94..18ff513 100644
--- a/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIfaceCallback.aidl
+++ b/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIfaceCallback.aidl
@@ -19,6 +19,106 @@
  */
 @utf8InCpp
 interface IIfaceCallback {
+	/** Used to indicate a non specific network event via |OnStateChanged|.*/
+	const int NETWORK_ID_INVALID = -1;
+
+	/** Various states of the interface reported by |OnStateChanged|.*/
+	/**
+	 * STATE_DISCONNECTED - Disconnected state
+	 *
+	 * This state indicates that client is not associated, but is likely to
+	 * start looking for an access point. This state is entered when a
+	 * connection is lost.
+	 */
+	const int STATE_DISCONNECTED = 0;
+	/**
+	 * STATE_INTERFACE_DISABLED - Interface disabled
+	 *
+	 * This state is entered if the network interface is disabled, e.g.,
+	 * due to rfkill. wpa_supplicant refuses any new operations that would
+	 * use the radio until the interface has been enabled.
+	 */
+	const int STATE_INTERFACE_DISABLED = 1;
+	/**
+	 * STATE_INACTIVE - Inactive state (wpa_supplicant disabled)
+	 *
+	 * This state is entered if there are no enabled networks in the
+	 * configuration. wpa_supplicant is not trying to associate with a new
+	 * network and external interaction (e.g., ctrl_iface call to add or
+	 * enable a network) is needed to start association.
+	 */
+	const int STATE_INACTIVE = 2;
+	/**
+	 * STATE_SCANNING - Scanning for a network
+	 *
+	 * This state is entered when wpa_supplicant starts scanning for a
+	 * network.
+	 */
+	const int STATE_SCANNING = 3;
+	/**
+	 * STATE_AUTHENTICATING - Trying to authenticate with a BSS/SSID
+	 *
+	 * This state is entered when wpa_supplicant has found a suitable BSS
+	 * to authenticate with and the driver is configured to try to
+	 * authenticate with this BSS. This state is used only with drivers
+	 * that use wpa_supplicant as the SME.
+	 */
+	const int STATE_AUTHENTICATING = 4;
+	/**
+	 * STATE_ASSOCIATING - Trying to associate with a BSS/SSID
+	 *
+	 * This state is entered when wpa_supplicant has found a suitable BSS
+	 * to associate with and the driver is configured to try to associate
+	 * with this BSS in ap_scan=1 mode. When using ap_scan=2 mode, this
+	 * state is entered when the driver is configured to try to associate
+	 * with a network using the configured SSID and security policy.
+	 */
+	const int STATE_ASSOCIATING = 5;
+	/**
+	 * STATE_ASSOCIATED - Association completed
+	 *
+	 * This state is entered when the driver reports that association has
+	 * been successfully completed with an AP. If IEEE 802.1X is used
+	 * (with or without WPA/WPA2), wpa_supplicant remains in this state
+	 * until the IEEE 802.1X/EAPOL authentication has been completed.
+	 */
+	const int STATE_ASSOCIATED = 6;
+	/**
+	 * STATE_4WAY_HANDSHAKE - WPA 4-Way Key Handshake in progress
+	 *
+	 * This state is entered when WPA/WPA2 4-Way Handshake is started. In
+	 * case of WPA-PSK, this happens when receiving the first EAPOL-Key
+	 * frame after association. In case of WPA-EAP, this state is entered
+	 * when the IEEE 802.1X/EAPOL authentication has been completed.
+	 */
+	const int STATE_4WAY_HANDSHAKE = 7;
+	/**
+	 * STATE_GROUP_HANDSHAKE - WPA Group Key Handshake in progress
+	 *
+	 * This state is entered when 4-Way Key Handshake has been completed
+	 * (i.e., when the supplicant sends out message 4/4) and when Group
+	 * Key rekeying is started by the AP (i.e., when supplicant receives
+	 * message 1/2).
+	 */
+	const int STATE_GROUP_HANDSHAKE = 8;
+	/**
+	 * STATE_COMPLETED - All authentication completed
+	 *
+	 * This state is entered when the full authentication process is
+	 * completed. In case of WPA2, this happens when the 4-Way Handshake is
+	 * successfully completed. With WPA, this state is entered after the
+	 * Group Key Handshake; with IEEE 802.1X (non-WPA) connection is
+	 * completed after dynamic keys are received (or if not used, after
+	 * the EAP authentication has been completed). With static WEP keys and
+	 * plaintext connections, this state is entered when an association
+	 * has been completed.
+	 *
+	 * This state indicates that the supplicant has completed its
+	 * processing for the association phase and that data connection is
+	 * fully configured.
+	 */
+	const int STATE_COMPLETED = 9;
+
 	/**
 	 * Used to indicate that a new network has been added.
 	 *
@@ -31,5 +131,26 @@
 	 *
 	 * @param id Network ID allocated to the corresponding network.
 	 */
-	oneway void OnNetworkRemoved(int network_id);
+	oneway void OnNetworkRemoved(int id);
+
+	/**
+	 * Used to indicate a state change event on this particular iface. This
+	 * event may be triggered by a particular network in which case the
+	 * |network_id|, |ssid|, |bssid| parameters will indicate the parameters
+	 * of the network/AP which cased this state transition.
+	 *
+	 * @param new_state New State of the interface.  This will be one of
+	 *        the |STATE_|* values above.
+	 * @param bssid BSSID of the corresponding AP which caused this state
+	 *        change event. This will be empty if this event is not specific
+	 *        to a particular network.
+	 * @param network_id ID of the corresponding network which caused this
+	 *        state change event. This will be |INVALID_NETWORK_ID| if this
+	 *        event is not specific to a particular network.
+	 * @param ssid SSID of the corresponding network which caused this state
+	 *        change event. This will be empty if this event is not specific
+	 *        to a particular network.
+	 */
+	oneway void OnStateChanged(
+	    int new_state, in byte[] bssid, int network_id, in byte[] ssid);
 }
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index b2b4225..61469dc 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -133,6 +133,8 @@
 		     wpa_ssid_txt(wpa_s->current_ssid->ssid,
 				  wpa_s->current_ssid->ssid_len) : "");
 #endif /* ANDROID */
+
+	wpas_binder_notify_state_changed(wpa_s);
 }