MBO-OCE feature support (phase 3)

This commit does the following:
1. Save mbo cellular data preference attribute and association retry delay
attribute in wpa_supplicant interface structure.
2. Notify framework about BSS transition request frame
handling status.

Bug: 139474288
Test: VTS test
Test: Manual
Change-Id: Icff9f89ee9eae8db62e777cf685fe8cb78879d49
diff --git a/wpa_supplicant/hidl/1.3/hidl.cpp b/wpa_supplicant/hidl/1.3/hidl.cpp
index a3efde4..4c2d434 100644
--- a/wpa_supplicant/hidl/1.3/hidl.cpp
+++ b/wpa_supplicant/hidl/1.3/hidl.cpp
@@ -861,3 +861,17 @@
 
 	hidl_manager->notifyPmkCacheAdded(wpa_s, pmksa_entry);
 }
+
+void wpas_hidl_notify_bss_tm_status(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s)
+		return;
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	wpa_printf(MSG_DEBUG, "Notifying BSS transition status");
+
+	hidl_manager->notifyBssTmStatus(wpa_s);
+}
diff --git a/wpa_supplicant/hidl/1.3/hidl.h b/wpa_supplicant/hidl/1.3/hidl.h
index 0db90a1..304a4d6 100644
--- a/wpa_supplicant/hidl/1.3/hidl.h
+++ b/wpa_supplicant/hidl/1.3/hidl.h
@@ -119,6 +119,7 @@
 	    const char *channel_list, unsigned short band_list[], int size);
 	void wpas_hidl_notify_pmk_cache_added(
 	    struct wpa_supplicant *wpas, struct rsn_pmksa_cache_entry *pmksa_entry);
+	void wpas_hidl_notify_bss_tm_status(struct wpa_supplicant *wpa_s);
 #else   // CONFIG_CTRL_IFACE_HIDL
 static inline int wpas_hidl_register_interface(struct wpa_supplicant *wpa_s)
 {
@@ -258,6 +259,8 @@
 static void wpas_hidl_notify_pmk_cache_added(struct wpa_supplicant *wpas,
 					     struct rsn_pmksa_cache_entry *pmksa_entry)
 {}
+void wpas_hidl_notify_bss_tm_status(struct wpa_supplicant *wpa_s)
+{}
 #endif  // CONFIG_CTRL_IFACE_HIDL
 
 #ifdef _cplusplus
diff --git a/wpa_supplicant/hidl/1.3/hidl_manager.cpp b/wpa_supplicant/hidl/1.3/hidl_manager.cpp
index 8b2be11..793757b 100644
--- a/wpa_supplicant/hidl/1.3/hidl_manager.cpp
+++ b/wpa_supplicant/hidl/1.3/hidl_manager.cpp
@@ -1597,6 +1597,158 @@
 	callWithEachStaIfaceCallbackDerived(hidl_ifname, func);
 }
 
+#ifdef CONFIG_WNM
+V1_3::ISupplicantStaIfaceCallback::BssTmStatusCode convertSupplicantBssTmStatusToHidl(
+    enum bss_trans_mgmt_status_code bss_tm_status)
+{
+	switch (bss_tm_status) {
+		case WNM_BSS_TM_ACCEPT:
+			return V1_3::ISupplicantStaIfaceCallback::BssTmStatusCode::ACCEPT;
+		case WNM_BSS_TM_REJECT_UNSPECIFIED:
+			return V1_3::ISupplicantStaIfaceCallback::
+			    BssTmStatusCode::REJECT_UNSPECIFIED;
+		case WNM_BSS_TM_REJECT_INSUFFICIENT_BEACON:
+			return V1_3::ISupplicantStaIfaceCallback::
+			    BssTmStatusCode::REJECT_INSUFFICIENT_BEACON;
+		case WNM_BSS_TM_REJECT_INSUFFICIENT_CAPABITY:
+			return V1_3::ISupplicantStaIfaceCallback::
+			    BssTmStatusCode::REJECT_INSUFFICIENT_CAPABITY;
+		case WNM_BSS_TM_REJECT_UNDESIRED:
+			return V1_3::ISupplicantStaIfaceCallback::
+			    BssTmStatusCode::REJECT_BSS_TERMINATION_UNDESIRED;
+		case WNM_BSS_TM_REJECT_DELAY_REQUEST:
+			return V1_3::ISupplicantStaIfaceCallback::
+			    BssTmStatusCode::REJECT_BSS_TERMINATION_DELAY_REQUEST;
+		case WNM_BSS_TM_REJECT_STA_CANDIDATE_LIST_PROVIDED:
+			return V1_3::ISupplicantStaIfaceCallback::
+			    BssTmStatusCode::REJECT_STA_CANDIDATE_LIST_PROVIDED;
+		case WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES:
+			return V1_3::ISupplicantStaIfaceCallback::
+			    BssTmStatusCode::REJECT_NO_SUITABLE_CANDIDATES;
+		case WNM_BSS_TM_REJECT_LEAVING_ESS:
+			return V1_3::ISupplicantStaIfaceCallback::
+			    BssTmStatusCode::REJECT_LEAVING_ESS;
+		default:
+			return V1_3::ISupplicantStaIfaceCallback::
+			    BssTmStatusCode::REJECT_UNSPECIFIED;
+	}
+}
+
+uint32_t setBssTmDataFlagsMask(struct wpa_supplicant *wpa_s)
+{
+	uint32_t flags = 0;
+
+	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
+		flags |= V1_3::ISupplicantStaIfaceCallback::
+		    BssTmDataFlagsMask::WNM_MODE_BSS_TERMINATION_INCLUDED;
+	}
+	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
+		flags |= V1_3::ISupplicantStaIfaceCallback::
+		    BssTmDataFlagsMask::WNM_MODE_ESS_DISASSOCIATION_IMMINENT;
+	}
+	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
+		flags |= V1_3::ISupplicantStaIfaceCallback::
+		    BssTmDataFlagsMask::WNM_MODE_DISASSOCIATION_IMMINENT;
+	}
+	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ABRIDGED) {
+		flags |= V1_3::ISupplicantStaIfaceCallback::
+		    BssTmDataFlagsMask::WNM_MODE_ABRIDGED;
+	}
+	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
+		flags |= V1_3::ISupplicantStaIfaceCallback::
+		    BssTmDataFlagsMask::WNM_MODE_PREFERRED_CANDIDATE_LIST_INCLUDED;
+	}
+#ifdef CONFIG_MBO
+	if (wpa_s->wnm_mbo_assoc_retry_delay_present) {
+		flags |= V1_3::ISupplicantStaIfaceCallback::
+		    BssTmDataFlagsMask::MBO_ASSOC_RETRY_DELAY_INCLUDED;
+	}
+	if (wpa_s->wnm_mbo_trans_reason_present) {
+		flags |= V1_3::ISupplicantStaIfaceCallback::
+		    BssTmDataFlagsMask::MBO_TRANSITION_REASON_CODE_INCLUDED;
+	}
+	if (wpa_s->wnm_mbo_cell_pref_present) {
+		flags |= V1_3::ISupplicantStaIfaceCallback::
+		    BssTmDataFlagsMask::MBO_CELLULAR_DATA_CONNECTION_PREFERENCE_INCLUDED;
+	}
+#endif
+	return flags;
+}
+
+uint32_t getBssTmDataAssocRetryDelayMs(struct wpa_supplicant *wpa_s)
+{
+	uint32_t beacon_int;
+	uint32_t duration_ms = 0;
+
+	if (wpa_s->current_bss)
+		beacon_int = wpa_s->current_bss->beacon_int;
+	else
+		beacon_int = 100; /* best guess */
+
+	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
+		//wnm_bss_termination_duration contains 12 bytes of BSS
+		//termination duration subelement. Format of IE is
+		// Sub eid | Length | BSS termination TSF | Duration
+		//    1         1             8                2
+		// Duration indicates number of minutes for which BSS is not
+		// present.
+		duration_ms = WPA_GET_LE16(wpa_s->wnm_bss_termination_duration + 10);
+		// minutes to milliseconds
+		duration_ms = duration_ms * 60 * 1000;
+	} else if ((wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT)
+		   || (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT)) {
+		// number of tbtts to milliseconds
+		duration_ms = wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125;
+#ifdef CONFIG_MBO
+		if (wpa_s->wnm_mbo_assoc_retry_delay_present) {
+			// number of seconds to milliseconds
+			duration_ms = wpa_s->wnm_mbo_assoc_retry_delay_sec * 1000;
+		}
+#endif
+	}
+
+	return duration_ms;
+}
+#endif
+
+/**
+ * Notify listener about the status of BSS transition management
+ * request frame handling.
+ *
+ * @param wpa_s |wpa_supplicant| struct corresponding to the interface on which
+ * the network is present.
+ */
+void HidlManager::notifyBssTmStatus(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_WNM
+	std::string hidl_ifname = wpa_s->ifname;
+	V1_3::ISupplicantStaIfaceCallback::BssTmData hidl_bsstm_data = {};
+
+	hidl_bsstm_data.status = convertSupplicantBssTmStatusToHidl(wpa_s->bss_tm_status);
+	hidl_bsstm_data.flags = setBssTmDataFlagsMask(wpa_s);
+	hidl_bsstm_data.assocRetryDelayMs = getBssTmDataAssocRetryDelayMs(wpa_s);
+#ifdef CONFIG_MBO
+	if (wpa_s->wnm_mbo_cell_pref_present) {
+		hidl_bsstm_data.mboCellPreference = static_cast
+		    <V1_3::ISupplicantStaIfaceCallback::MboCellularDataConnectionPrefValue>
+		    (wpa_s->wnm_mbo_cell_preference);
+	}
+	if (wpa_s->wnm_mbo_trans_reason_present) {
+		hidl_bsstm_data.mboTransitionReason =
+		    static_cast<V1_3::ISupplicantStaIfaceCallback::MboTransitionReasonCode>
+		    (wpa_s->wnm_mbo_transition_reason);
+	}
+#endif
+
+	const std::function<
+	    Return<void>(android::sp<V1_3::ISupplicantStaIfaceCallback>)>
+	    func = std::bind(
+		&V1_3::ISupplicantStaIfaceCallback::onBssTmHandlingDone,
+		std::placeholders::_1, hidl_bsstm_data);
+	callWithEachStaIfaceCallbackDerived(hidl_ifname, func);
+#endif
+}
+
 /**
  * Retrieve the |ISupplicantP2pIface| hidl object reference using the provided
  * ifname.
diff --git a/wpa_supplicant/hidl/1.3/hidl_manager.h b/wpa_supplicant/hidl/1.3/hidl_manager.h
index 61601bd..e49e28d 100644
--- a/wpa_supplicant/hidl/1.3/hidl_manager.h
+++ b/wpa_supplicant/hidl/1.3/hidl_manager.h
@@ -144,6 +144,7 @@
 			android::hardware::wifi::supplicant::V1_3::DppProgressCode code);
 	void notifyPmkCacheAdded(struct wpa_supplicant *wpa_s,
 			struct rsn_pmksa_cache_entry *pmksa_entry);
+	void notifyBssTmStatus(struct wpa_supplicant *wpa_s);
 
 	// Methods called from hidl objects.
 	void notifyExtRadioWorkStart(struct wpa_supplicant *wpa_s, uint32_t id);
diff --git a/wpa_supplicant/mbo.c b/wpa_supplicant/mbo.c
index 3df86ef..8ac73ef 100644
--- a/wpa_supplicant/mbo.c
+++ b/wpa_supplicant/mbo.c
@@ -464,9 +464,8 @@
 void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie,
 			   size_t len)
 {
-	const u8 *pos, *cell_pref = NULL;
+	const u8 *pos;
 	u8 id, elen;
-	u16 disallowed_sec = 0;
 
 	if (len <= 4 || WPA_GET_BE24(mbo_ie) != OUI_WFA ||
 	    mbo_ie[3] != MBO_OUI_TYPE)
@@ -489,11 +488,14 @@
 				goto fail;
 
 			if (wpa_s->conf->mbo_cell_capa ==
-			    MBO_CELL_CAPA_AVAILABLE)
-				cell_pref = pos;
-			else
+			    MBO_CELL_CAPA_AVAILABLE) {
+				wpa_s->wnm_mbo_cell_pref_present = 1;
+				wpa_s->wnm_mbo_cell_preference = *pos;
+			} else {
 				wpa_printf(MSG_DEBUG,
-					   "MBO: Station does not support Cellular data connection");
+					   "MBO: Station does not support "
+					   "Cellular data connection");
+			}
 			break;
 		case MBO_ATTR_ID_TRANSITION_REASON:
 			if (elen != 1)
@@ -509,17 +511,20 @@
 			if (wpa_s->wnm_mode &
 			    WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
 				wpa_printf(MSG_DEBUG,
-					   "MBO: Unexpected association retry delay, BSS is terminating");
+					   "MBO: Unexpected association retry delay, "
+					   "BSS is terminating");
 				goto fail;
 			} else if (wpa_s->wnm_mode &
 				   WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
-				disallowed_sec = WPA_GET_LE16(pos);
+				wpa_s->wnm_mbo_assoc_retry_delay_present = 1;
+				wpa_s->wnm_mbo_assoc_retry_delay_sec = WPA_GET_LE16(pos);
 				wpa_printf(MSG_DEBUG,
 					   "MBO: Association retry delay: %u",
-					   disallowed_sec);
+					   wpa_s->wnm_mbo_assoc_retry_delay_sec);
 			} else {
 				wpa_printf(MSG_DEBUG,
-					   "MBO: Association retry delay attribute not in disassoc imminent mode");
+					   "MBO: Association retry delay attribute "
+					   "not in disassoc imminent mode");
 			}
 
 			break;
@@ -542,17 +547,17 @@
 		len -= elen;
 	}
 
-	if (cell_pref)
+	if (wpa_s->wnm_mbo_cell_pref_present)
 		wpa_msg(wpa_s, MSG_INFO, MBO_CELL_PREFERENCE "preference=%u",
-			*cell_pref);
+			wpa_s->wnm_mbo_cell_preference);
 
 	if (wpa_s->wnm_mbo_trans_reason_present)
 		wpa_msg(wpa_s, MSG_INFO, MBO_TRANSITION_REASON "reason=%u",
 			wpa_s->wnm_mbo_transition_reason);
 
-	if (disallowed_sec && wpa_s->current_bss)
+	if (wpa_s->wnm_mbo_assoc_retry_delay_sec && wpa_s->current_bss)
 		wpa_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid,
-				     disallowed_sec, 0);
+				     wpa_s->wnm_mbo_assoc_retry_delay_sec, 0);
 
 	return;
 fail:
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 206b488..56eb62a 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -198,6 +198,10 @@
 		return;
 
 	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_BSS_TM_STATUS);
+
+#ifdef CONFIG_WNM
+	wpas_hidl_notify_bss_tm_status(wpa_s);
+#endif
 }
 
 
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 270be9e..1c0399f 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -1378,6 +1378,10 @@
 #ifdef CONFIG_MBO
 	wpa_s->wnm_mbo_trans_reason_present = 0;
 	wpa_s->wnm_mbo_transition_reason = 0;
+	wpa_s->wnm_mbo_cell_pref_present = 0;
+	wpa_s->wnm_mbo_cell_preference = 0;
+	wpa_s->wnm_mbo_assoc_retry_delay_present = 0;
+	wpa_s->wnm_mbo_assoc_retry_delay_sec = 0;
 #endif /* CONFIG_MBO */
 
 	if (wpa_s->current_bss)
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index c5d2535..b6fb491 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1093,7 +1093,11 @@
 	u8 coloc_intf_timeout;
 #ifdef CONFIG_MBO
 	unsigned int wnm_mbo_trans_reason_present:1;
+	unsigned int wnm_mbo_cell_pref_present:1;
+	unsigned int wnm_mbo_assoc_retry_delay_present:1;
 	u8 wnm_mbo_transition_reason;
+	u8 wnm_mbo_cell_preference;
+	u16 wnm_mbo_assoc_retry_delay_sec;
 #endif /* CONFIG_MBO */
 #endif /* CONFIG_WNM */