Merge "Fix issue caused by NULL at msg pointer"
diff --git a/src/common/dpp.h b/src/common/dpp.h
index ee29a08..3094be8 100644
--- a/src/common/dpp.h
+++ b/src/common/dpp.h
@@ -296,6 +296,7 @@
 	enum dpp_status_error auth_resp_status;
 	enum dpp_status_error conf_resp_status;
 	enum dpp_status_error force_conf_resp_status;
+	enum dpp_status_error conn_result_status;
 	u8 peer_mac_addr[ETH_ALEN];
 	u8 i_nonce[DPP_MAX_NONCE_LEN];
 	u8 r_nonce[DPP_MAX_NONCE_LEN];
@@ -340,6 +341,7 @@
 	int connect_on_tx_status;
 	int waiting_conf_result;
 	int waiting_conn_status_result;
+	int tx_conn_status_result_started;
 	int auth_success;
 	bool reconfig_success;
 	struct wpabuf *conf_req;
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index db46d95..a7dee37 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -1731,6 +1731,9 @@
 		wpa_hexdump_ascii(MSG_DEBUG,
 				  "EAP: using IMSI privacy anonymous identity",
 				  identity, identity_len);
+	} else if (config->strict_conservative_peer_mode) {
+		wpa_printf(MSG_DEBUG, "EAP: never use real identity in conservative peer mode.");
+		return NULL;
 	} else {
 		identity = config->identity;
 		identity_len = config->identity_len;
@@ -2816,6 +2819,24 @@
 	return config->identity;
 }
 
+
+/**
+ * eap_get_config_strict_conservative_peer_mode - get the value of
+ * strict conservative peer mode in eap_peer_config.
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+*/
+int eap_get_config_strict_conservative_peer_mode(struct eap_sm *sm)
+{
+	struct eap_peer_config *config;
+	config = eap_get_config(sm);
+	if (config) {
+		return config->strict_conservative_peer_mode;
+	}
+
+	return 0;
+}
+
+
 static const u8 * strnchr(const u8 *str, size_t len, u8 needle) {
 	const u8 *cur = str;
 
@@ -3080,6 +3101,19 @@
 	return sm ? sm->eapKeyAvailable : 0;
 }
 
+/**
+ * eap_notify_permanent_id_req_denied - Notify that the AT_PERMANENT_ID_REQ
+ * is denied from eap_peer when the strict conservative mode is enabled.
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+*/
+void eap_notify_permanent_id_req_denied(struct eap_sm *sm)
+{
+	if (!sm || !sm->eapol_cb->notify_permanent_id_req_denied)
+		return;
+
+	sm->eapol_cb->notify_permanent_id_req_denied(sm->eapol_ctx);
+}
+
 
 /**
  * eap_notify_success - Notify EAP state machine about external success trigger
diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h
index aae1a41..8f83d0b 100644
--- a/src/eap_peer/eap.h
+++ b/src/eap_peer/eap.h
@@ -233,6 +233,15 @@
 			    const char *cert_hash);
 
 	/**
+	 * notify_permanent_id_req_denied - Notify that the
+	 * AT_PERMANENT_ID_REQ from the server was denied. This
+	 * notification happens when the peer is in strict
+	 * conservative mode.
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	*/
+	void (*notify_permanent_id_req_denied)(void* ctx);
+
+	/**
 	 * notify_status - Notification of the current EAP state
 	 * @ctx: eapol_ctx from eap_peer_sm_init() call
 	 * @status: Step in the process of EAP authentication
@@ -366,11 +375,13 @@
 void eap_set_force_disabled(struct eap_sm *sm, int disabled);
 void eap_set_external_sim(struct eap_sm *sm, int external_sim);
 int eap_key_available(struct eap_sm *sm);
+void eap_notify_permanent_id_req_denied(struct eap_sm *sm);
 void eap_notify_success(struct eap_sm *sm);
 void eap_notify_lower_layer_success(struct eap_sm *sm);
 const u8 * eap_get_eapSessionId(struct eap_sm *sm, size_t *len);
 const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len);
 struct wpabuf * eap_get_eapRespData(struct eap_sm *sm);
+int eap_get_config_strict_conservative_peer_mode(struct eap_sm *sm);
 void eap_register_scard_ctx(struct eap_sm *sm, void *ctx);
 void eap_invalidate_cached_session(struct eap_sm *sm);
 
diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c
index 7f660e6..49338cf 100644
--- a/src/eap_peer/eap_aka.c
+++ b/src/eap_peer/eap_aka.c
@@ -708,6 +708,12 @@
 		identity_len = data->pseudonym_len;
 		eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID);
 	} else if (id_req != NO_ID_REQ) {
+		if (id_req == PERMANENT_ID && eap_get_config_strict_conservative_peer_mode(sm)) {
+			wpa_printf(MSG_INFO, "EAP-AKA: permanent_id_req is denied in "
+				   "the strict conservative peer mode");
+			eap_notify_permanent_id_req_denied(sm);
+			return eap_aka_client_error(data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+		}
 		identity = eap_get_config_identity(sm, &identity_len);
 		if (identity) {
 			int ids = CLEAR_PSEUDONYM | CLEAR_REAUTH_ID;
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
index 26744ab..59fac90 100644
--- a/src/eap_peer/eap_config.h
+++ b/src/eap_peer/eap_config.h
@@ -338,6 +338,18 @@
 	char *imsi_privacy_attr;
 
 	/**
+	 * strict_conservative_peer_mode - Whether the strict conservative peer
+	 * mode is enabled or not
+	 *
+	 * This field is used to handle the reponse of AT_PERMANENT_ID_REQ
+	 * for EAP-SIM/AKA/AKA', in conservative peer mode, a client error would
+	 * be sent to the server, but it allows to send the permanent identity
+	 * in some special cases according to 4.6.2 of RFC 4187; With the strict
+	 * mode, it never sends the permanent identity to server for privacy concern.
+	 */
+	int strict_conservative_peer_mode;
+
+	/**
 	 * machine_identity - EAP Identity for machine credential
 	 *
 	 * This field is used to set the machine identity or NAI for cases where
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
index 71e9108..6f18ebf 100644
--- a/src/eap_peer/eap_sim.c
+++ b/src/eap_peer/eap_sim.c
@@ -576,6 +576,12 @@
 		identity_len = data->pseudonym_len;
 		eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID);
 	} else if (id_req != NO_ID_REQ) {
+		if (id_req == PERMANENT_ID && eap_get_config_strict_conservative_peer_mode(sm)) {
+			wpa_printf(MSG_INFO, "EAP-SIM: permanent_id_req is denied in "
+				   "the strict conservative peer mode");
+			eap_notify_permanent_id_req_denied(sm);
+			return  eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+		}
 		identity = eap_get_config_identity(sm, &identity_len);
 		if (identity) {
 			int ids = CLEAR_PSEUDONYM | CLEAR_REAUTH_ID;
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index 6173960..a0bc6ab 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -2031,6 +2031,13 @@
 		sm->ctx->cert_cb(sm->ctx->ctx, cert, cert_hash);
 }
 
+static void eapol_sm_notify_permanent_id_req_denied(void *ctx)
+{
+	struct eapol_sm *sm = ctx;
+	if (sm->ctx->permanent_id_req_denied_cb)
+		sm->ctx->permanent_id_req_denied_cb(sm->ctx->ctx);
+}
+
 
 static void eapol_sm_notify_status(void *ctx, const char *status,
 				   const char *parameter)
@@ -2118,6 +2125,7 @@
 	eapol_sm_notify_pending,
 	eapol_sm_eap_param_needed,
 	eapol_sm_notify_cert,
+	eapol_sm_notify_permanent_id_req_denied,
 	eapol_sm_notify_status,
 	eapol_sm_notify_eap_error,
 #ifdef CONFIG_EAP_PROXY
diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h
index bbe2b6f..ad94cf5 100644
--- a/src/eapol_supp/eapol_supp_sm.h
+++ b/src/eapol_supp/eapol_supp_sm.h
@@ -255,6 +255,14 @@
 			const char *cert_hash);
 
 	/**
+	 * permanent_id_req_denied_cb - Notify that the AT_PERMANENT_ID_REQ
+	 * from the server was denied. This notification happens when the
+	 * peer is in the strict conservative mode.
+	 * @ctx: Callback context (ctx)
+	*/
+	void (*permanent_id_req_denied_cb)(void *ctx);
+
+	/**
 	 * cert_in_cb - Include server certificates in callback
 	 */
 	int cert_in_cb;
diff --git a/wpa_supplicant/aidl/aidl.cpp b/wpa_supplicant/aidl/aidl.cpp
index a7945cc..6bc13ad 100644
--- a/wpa_supplicant/aidl/aidl.cpp
+++ b/wpa_supplicant/aidl/aidl.cpp
@@ -186,6 +186,21 @@
 		wpa_s, ssid, rtype, default_txt);
 }
 
+void wpas_aidl_notify_permanent_id_req_denied(
+		struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s || !wpa_s->global->aidl)
+		return;
+
+	wpa_printf(MSG_DEBUG, "Notifying permanent_id_req denied to aidl control.");
+
+	AidlManager *aidl_manager = AidlManager::getInstance();
+	if (!aidl_manager)
+		return;
+
+	return aidl_manager->notifyPermanentIdReqDenied(wpa_s);
+}
+
 void wpas_aidl_notify_anqp_query_done(
 	struct wpa_supplicant *wpa_s, const u8 *bssid, const char *result,
 	const struct wpa_bss_anqp *anqp)
@@ -671,7 +686,7 @@
 }
 
 void wpas_aidl_notify_dpp_config_received(struct wpa_supplicant *wpa_s,
-		struct wpa_ssid *ssid)
+		struct wpa_ssid *ssid, bool conn_status_requested)
 {
 	if (!wpa_s || !ssid)
 		return;
@@ -684,7 +699,7 @@
 	if (!aidl_manager)
 		return;
 
-	aidl_manager->notifyDppConfigReceived(wpa_s, ssid);
+	aidl_manager->notifyDppConfigReceived(wpa_s, ssid, conn_status_requested);
 }
 
 void wpas_aidl_notify_dpp_config_sent(struct wpa_supplicant *wpa_s)
@@ -692,6 +707,21 @@
 	wpas_aidl_notify_dpp_success(wpa_s, DppEventType::CONFIGURATION_SENT);
 }
 
+void wpas_aidl_notify_dpp_connection_status_sent(struct wpa_supplicant *wpa_s,
+                                                 enum dpp_status_error result)
+{
+	if (!wpa_s)
+		return;
+
+	wpa_printf(MSG_DEBUG, "wpas_aidl_notify_dpp_connection_status_sent %d ", result);
+
+	AidlManager *aidl_manager = AidlManager::getInstance();
+	if (!aidl_manager)
+		return;
+
+	aidl_manager->notifyDppConnectionStatusSent(wpa_s, result);
+}
+
 /* DPP Progress notifications */
 void wpas_aidl_notify_dpp_auth_success(struct wpa_supplicant *wpa_s)
 {
diff --git a/wpa_supplicant/aidl/aidl.h b/wpa_supplicant/aidl/aidl.h
index 2f3c7a0..40eb860 100644
--- a/wpa_supplicant/aidl/aidl.h
+++ b/wpa_supplicant/aidl/aidl.h
@@ -37,6 +37,7 @@
 	int wpas_aidl_notify_network_request(
 		struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 		enum wpa_ctrl_req_type rtype, const char *default_txt);
+	void wpas_aidl_notify_permanent_id_req_denied(struct wpa_supplicant *wpa_s);
 	void wpas_aidl_notify_anqp_query_done(
 		struct wpa_supplicant *wpa_s, const u8 *bssid, const char *result,
 		const struct wpa_bss_anqp *anqp);
@@ -103,8 +104,10 @@
 	void wpas_aidl_notify_eap_error(
 		struct wpa_supplicant *wpa_s, int error_code);
 	void wpas_aidl_notify_dpp_config_received(struct wpa_supplicant *wpa_s,
-			struct wpa_ssid *ssid);
+		struct wpa_ssid *ssid, bool conn_status_requested);
 	void wpas_aidl_notify_dpp_config_sent(struct wpa_supplicant *wpa_s);
+	void wpas_aidl_notify_dpp_connection_status_sent(struct wpa_supplicant *wpa_s,
+		enum dpp_status_error result);
 	void wpas_aidl_notify_dpp_auth_success(struct wpa_supplicant *wpa_s);
 	void wpas_aidl_notify_dpp_resp_pending(struct wpa_supplicant *wpa_s);
 	void wpas_aidl_notify_dpp_not_compatible(struct wpa_supplicant *wpa_s);
@@ -171,6 +174,8 @@
 {
 	return 0;
 }
+static void wpas_aidl_notify_permanent_id_req_denied(struct wpa_supplicant *wpa_s)
+{}
 static void wpas_aidl_notify_anqp_query_done(
 	struct wpa_supplicant *wpa_s, const u8 *bssid, const char *result,
 	const struct wpa_bss_anqp *anqp)
@@ -252,12 +257,13 @@
 	struct wpa_supplicant *wpa_s, int error_code)
 {}
 static void wpas_aidl_notify_dpp_config_received(struct wpa_supplicant *wpa_s,
-		struct wpa_ssid *ssid)
+	struct wpa_ssid *ssid, bool conn_status_requested)
 {}
-static void wpas_aidl_notify_dpp_config_received(struct wpa_supplicant *wpa_s,
-		struct wpa_ssid *ssid);
 static void wpas_aidl_notify_dpp_config_sent(struct wpa_supplicant *wpa_s)
 {}
+void wpas_aidl_notify_dpp_connection_status_sent(struct wpa_supplicant *wpa_s,
+	enum dpp_status_error result)
+{}
 static void wpas_aidl_notify_dpp_auth_success(struct wpa_supplicant *wpa_s)
 {}
 static void wpas_aidl_notify_dpp_resp_pending(struct wpa_supplicant *wpa_s)
diff --git a/wpa_supplicant/aidl/aidl_manager.cpp b/wpa_supplicant/aidl/aidl_manager.cpp
index 33ad650..e7cb3a6 100644
--- a/wpa_supplicant/aidl/aidl_manager.cpp
+++ b/wpa_supplicant/aidl/aidl_manager.cpp
@@ -762,6 +762,30 @@
 }
 
 /**
+ * Notify that the AT_PERMANENT_ID_REQ is denied from eap_peer when the strict
+ * conservative peer mode is enabled.
+ *
+ * @param wpa_s |wpa_supplicant| struct corresponding to the interface on which
+ * the network is present.
+*/
+void AidlManager::notifyPermanentIdReqDenied(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->current_ssid) {
+		wpa_printf(MSG_ERROR, "Current network NULL. Drop permanent_id_req_denied event!");
+		return;
+	}
+	struct wpa_ssid *current_ssid = wpa_s->current_ssid;
+
+	callWithEachStaNetworkCallback(
+			misc_utils::charBufToString(wpa_s->ifname),
+			current_ssid->id,
+			std::bind(
+			&ISupplicantStaNetworkCallback::
+				onPermanentIdReqDenied,
+			std::placeholders::_1));
+}
+
+/**
  * Notify all listeners about the end of an ANQP query.
  *
  * @param wpa_s |wpa_supplicant| struct corresponding to the interface.
@@ -1570,19 +1594,18 @@
  * @param config Configuration object
  */
 void AidlManager::notifyDppConfigReceived(struct wpa_supplicant *wpa_s,
-		struct wpa_ssid *config)
+		struct wpa_ssid *config, bool conn_status_requested)
 {
-	DppAkm securityAkm;
-	DppConnectionKeys aidl_keys{};
 	std::string aidl_ifname = misc_utils::charBufToString(wpa_s->ifname);
+	DppConfigurationData aidl_dpp_config_data = {};
 
 	if ((config->key_mgmt & WPA_KEY_MGMT_SAE) &&
 			(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) {
-		securityAkm = DppAkm::SAE;
+		aidl_dpp_config_data.securityAkm = DppAkm::SAE;
 	} else if (config->key_mgmt & WPA_KEY_MGMT_PSK) {
-			securityAkm = DppAkm::PSK;
+		aidl_dpp_config_data.securityAkm = DppAkm::PSK;
 	} else if (config->key_mgmt & WPA_KEY_MGMT_DPP) {
-			securityAkm = DppAkm::DPP;
+		aidl_dpp_config_data.securityAkm = DppAkm::DPP;
 	} else {
 		/* Unsupported AKM */
 		wpa_printf(MSG_ERROR, "DPP: Error: Unsupported AKM 0x%X",
@@ -1591,29 +1614,31 @@
 		return;
 	}
 
-	std::string passphrase = misc_utils::charBufToString(config->passphrase);
+	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,
 		config->ssid + config->ssid_len);
+	aidl_dpp_config_data.ssid = aidl_ssid;
 
-	if (securityAkm == DppAkm::DPP) {
+	if (aidl_dpp_config_data.securityAkm == DppAkm::DPP) {
 		std::string connector_str = misc_utils::charBufToString(config->dpp_connector);
-		aidl_keys.connector = std::vector<uint8_t>(connector_str.begin(),
-			connector_str.end());
-		aidl_keys.cSign = byteArrToVec(config->dpp_csign, config->dpp_csign_len);
-		aidl_keys.netAccessKey = byteArrToVec(config->dpp_netaccesskey,
-			config->dpp_netaccesskey_len);
+		aidl_dpp_config_data.dppConnectionKeys.connector
+			= std::vector<uint8_t>(connector_str.begin(), connector_str.end());
+		aidl_dpp_config_data.dppConnectionKeys.cSign
+			= byteArrToVec(config->dpp_csign, config->dpp_csign_len);
+		aidl_dpp_config_data.dppConnectionKeys.netAccessKey
+			= byteArrToVec(config->dpp_netaccesskey, config->dpp_netaccesskey_len);
 	}
+	aidl_dpp_config_data.connStatusRequested = conn_status_requested;
 
 	/* At this point, the network is already registered, notify about new
 	 * received configuration
 	 */
 	callWithEachStaIfaceCallback(aidl_ifname,
 			std::bind(
-					&ISupplicantStaIfaceCallback::onDppSuccessConfigReceived,
-					std::placeholders::_1, aidl_ssid, passphrase,
-					byteArrToVec(config->psk, 32), securityAkm,
-					aidl_keys));
+					&ISupplicantStaIfaceCallback::onDppConfigReceived,
+					std::placeholders::_1, aidl_dpp_config_data));
 }
 
 /**
@@ -1630,6 +1655,55 @@
 					std::placeholders::_1));
 }
 
+DppStatusErrorCode convertSupplicantDppStatusErrorCodeToAidl(
+	enum dpp_status_error code)
+{
+	switch (code) {
+		case DPP_STATUS_OK:
+			return DppStatusErrorCode::SUCCESS;
+		case DPP_STATUS_NOT_COMPATIBLE:
+			return DppStatusErrorCode::NOT_COMPATIBLE;
+		case DPP_STATUS_AUTH_FAILURE:
+			return DppStatusErrorCode::AUTH_FAILURE;
+		case DPP_STATUS_UNWRAP_FAILURE:
+			return DppStatusErrorCode::UNWRAP_FAILURE;
+		case DPP_STATUS_BAD_GROUP:
+			return DppStatusErrorCode::BAD_GROUP;
+		case DPP_STATUS_CONFIGURE_FAILURE:
+			return DppStatusErrorCode::CONFIGURE_FAILURE;
+		case DPP_STATUS_RESPONSE_PENDING:
+			return DppStatusErrorCode::RESPONSE_PENDING;
+		case DPP_STATUS_INVALID_CONNECTOR:
+			return DppStatusErrorCode::INVALID_CONNECTOR;
+		case DPP_STATUS_CONFIG_REJECTED:
+			return DppStatusErrorCode::CONFIG_REJECTED;
+		case DPP_STATUS_NO_MATCH:
+			return DppStatusErrorCode::NO_MATCH;
+		case DPP_STATUS_NO_AP:
+			return DppStatusErrorCode::NO_AP;
+		case DPP_STATUS_CONFIGURE_PENDING:
+			return DppStatusErrorCode::CONFIGURE_PENDING;
+		case DPP_STATUS_CSR_NEEDED:
+			return DppStatusErrorCode::CSR_NEEDED;
+		case DPP_STATUS_CSR_BAD:
+			return DppStatusErrorCode::CSR_BAD;
+		case DPP_STATUS_NEW_KEY_NEEDED:
+			return DppStatusErrorCode::NEW_KEY_NEEDED;
+		default:
+			return DppStatusErrorCode::UNKNOWN;
+	}
+}
+
+void AidlManager::notifyDppConnectionStatusSent(struct wpa_supplicant *wpa_s,
+		enum dpp_status_error result)
+{
+	std::string aidl_ifname = misc_utils::charBufToString(wpa_s->ifname);
+	callWithEachStaIfaceCallback(aidl_ifname,
+			std::bind(&ISupplicantStaIfaceCallback::onDppConnectionStatusResultSent,
+					std::placeholders::_1,
+					convertSupplicantDppStatusErrorCodeToAidl(result)));
+}
+
 /**
  * Notify listener about a DPP failure event
  *
@@ -1917,24 +1991,33 @@
 	callWithEachStaIfaceCallback(misc_utils::charBufToString(wpa_s->ifname), func);
 }
 
-void AidlManager::notifyFrequencyChanged(struct wpa_supplicant *wpa_group_s, int frequency)
+void AidlManager::notifyFrequencyChanged(struct wpa_supplicant *wpa_s, int frequency)
 {
-	if (!wpa_group_s || !wpa_group_s->parent)
+	if (!wpa_s)
 		return;
 
-	// For group notifications, need to use the parent iface for callbacks.
-	struct wpa_supplicant *wpa_s = getTargetP2pIfaceForGroup(wpa_group_s);
-	if (!wpa_s) {
+	std::string aidl_ifname = misc_utils::charBufToString(wpa_s->ifname);
+	struct wpa_supplicant *wpa_p2pdev_s = getTargetP2pIfaceForGroup(wpa_s);
+	if (wpa_p2pdev_s) {
+		// Notify frequency changed event on P2P interface
+		const std::function<
+			ndk::ScopedAStatus(std::shared_ptr<ISupplicantP2pIfaceCallback>)>
+			func = std::bind(&ISupplicantP2pIfaceCallback::onGroupFrequencyChanged,
+			std::placeholders::_1, aidl_ifname, frequency);
+		// For group notifications, need to use the parent iface for callbacks.
+		callWithEachP2pIfaceCallback(misc_utils::charBufToString(wpa_p2pdev_s->ifname), func);
+	} else if (wpa_s->current_ssid) {
+		// Notify frequency changed event on STA interface
+		const std::function<
+			ndk::ScopedAStatus(std::shared_ptr<ISupplicantStaIfaceCallback>)>
+			func = std::bind(
+			&ISupplicantStaIfaceCallback::onBssFrequencyChanged,
+			std::placeholders::_1, frequency);
+		callWithEachStaIfaceCallback(aidl_ifname, func);
+	} else {
 		wpa_printf(MSG_INFO, "Drop frequency changed event");
 		return;
-	}
-
-	const std::function<
-		ndk::ScopedAStatus(std::shared_ptr<ISupplicantP2pIfaceCallback>)>
-		func = std::bind(&ISupplicantP2pIfaceCallback::onGroupFrequencyChanged,
-		std::placeholders::_1, misc_utils::charBufToString(wpa_group_s->ifname),
-		frequency);
-	callWithEachP2pIfaceCallback(misc_utils::charBufToString(wpa_s->ifname), func);
+        }
 }
 
 void AidlManager::notifyCertification(struct wpa_supplicant *wpa_s,
diff --git a/wpa_supplicant/aidl/aidl_manager.h b/wpa_supplicant/aidl/aidl_manager.h
index 1ed6899..5224ef2 100644
--- a/wpa_supplicant/aidl/aidl_manager.h
+++ b/wpa_supplicant/aidl/aidl_manager.h
@@ -61,6 +61,8 @@
 	int notifyNetworkRequest(
 		struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int type,
 		const char *param);
+	void notifyPermanentIdReqDenied(
+		struct wpa_supplicant *wpa_s);
 	void notifyAnqpQueryDone(
 		struct wpa_supplicant *wpa_s, const u8 *bssid, const char *result,
 		const struct wpa_bss_anqp *anqp);
@@ -125,8 +127,11 @@
 		const u8 *p2p_dev_addr);
 	void notifyEapError(struct wpa_supplicant *wpa_s, int error_code);
 	void notifyDppConfigReceived(struct wpa_supplicant *wpa_s,
-			struct wpa_ssid *config);
+		struct wpa_ssid *config,
+		bool conn_status_requested);
 	void notifyDppConfigSent(struct wpa_supplicant *wpa_s);
+	void notifyDppConnectionStatusSent(struct wpa_supplicant *wpa_s,
+		enum dpp_status_error result);
 	void notifyDppSuccess(struct wpa_supplicant *wpa_s, DppEventType code);
 	void notifyDppFailure(struct wpa_supplicant *wpa_s,
 			DppFailureCode code);
diff --git a/wpa_supplicant/aidl/sta_iface.cpp b/wpa_supplicant/aidl/sta_iface.cpp
index d88f6f0..eb3e559 100644
--- a/wpa_supplicant/aidl/sta_iface.cpp
+++ b/wpa_supplicant/aidl/sta_iface.cpp
@@ -1956,6 +1956,21 @@
 		wpa_printf(MSG_DEBUG, "Add MLO Link ID %d info", i);
 		link.linkId = i;
 		link.staLinkMacAddress.assign(wpa_s->links[i].addr, wpa_s->links[i].addr + ETH_ALEN);
+		// TODO (b/259710591): Once suppllicant implements TID-to-link
+		// mapping, copy it here. Mapping can be changed in two
+		// scenarios
+		//    1. Mandatory mapping from AP
+		//    2. Negotiated mapping
+		// After association, framework call this API to get
+		// MloLinksInfo. If there is an update in mapping later, notify
+		// framework on the change using the callback,
+		// ISupplicantStaIfaceCallback.onMloLinksInfoChanged() with
+		// reason code as TID_TO_LINK_MAP. In absence of an advertised
+		// mapping by the AP, a default TID-to-link mapping is assumed
+		// unless an individual TID-to-link mapping is successfully
+		// negotiated.
+		link.tidsUplinkMap = 0xFF;
+		link.tidsDownlinkMap = 0xFF;
 		linksInfo.links.push_back(link);
 	}
 
diff --git a/wpa_supplicant/aidl/sta_network.cpp b/wpa_supplicant/aidl/sta_network.cpp
index f9f980c..db13509 100644
--- a/wpa_supplicant/aidl/sta_network.cpp
+++ b/wpa_supplicant/aidl/sta_network.cpp
@@ -304,6 +304,15 @@
 		in_identity);
 }
 
+::ndk::ScopedAStatus StaNetwork::setStrictConservativePeerMode(
+	bool in_enable)
+{
+	return validateAndCall(
+		this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+		&StaNetwork::setStrictConservativePeerModeInternal,
+		in_enable);
+}
+
 ::ndk::ScopedAStatus StaNetwork::setEapAnonymousIdentity(
 	const std::vector<uint8_t>& in_identity)
 {
@@ -1246,6 +1255,13 @@
 	return ndk::ScopedAStatus::ok();
 }
 
+ndk::ScopedAStatus StaNetwork::setStrictConservativePeerModeInternal(bool enable)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	wpa_ssid->eap.strict_conservative_peer_mode = enable ? 1 : 0;
+	return ndk::ScopedAStatus::ok();
+}
+
 ndk::ScopedAStatus StaNetwork::setEapAnonymousIdentityInternal(
 	const std::vector<uint8_t> &identity)
 {
@@ -2163,10 +2179,19 @@
 	KeyMgmtMask mask)
 {
 	uint32_t key_mgmt_mask = static_cast<uint32_t>(mask);
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
 	if (key_mgmt_mask & ~kAllowedKeyMgmtMask) {
 		return createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID);
 	}
+#ifdef CONFIG_SAE
+	struct wpa_driver_capa capa;
+	int res = wpa_drv_get_capa(wpa_s, &capa);
+	if ((res == 0) && (key_mgmt_mask & WPA_KEY_MGMT_SAE) &&
+		(capa.key_mgmt_iftype[WPA_IF_STATION] & WPA_DRIVER_CAPA_KEY_MGMT_SAE_EXT_KEY)) {
+		key_mgmt_mask |= WPA_KEY_MGMT_SAE_EXT_KEY;
+	}
+#endif
 	setFastTransitionKeyMgmt(key_mgmt_mask);
 
 	if (key_mgmt_mask & WPA_KEY_MGMT_OWE) {
@@ -2500,6 +2525,11 @@
 			(capa.key_mgmt_iftype[WPA_IF_STATION] & WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE)) {
 			key_mgmt_mask |= WPA_KEY_MGMT_FT_SAE;
 		}
+		if ((key_mgmt_mask & WPA_KEY_MGMT_SAE_EXT_KEY) &&
+			(capa.key_mgmt_iftype[WPA_IF_STATION] &
+			    WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE_EXT_KEY)) {
+			key_mgmt_mask |= WPA_KEY_MGMT_FT_SAE_EXT_KEY;
+		}
 #endif
 #ifdef CONFIG_FILS
 		if ((key_mgmt_mask & WPA_KEY_MGMT_FILS_SHA256) &&
diff --git a/wpa_supplicant/aidl/sta_network.h b/wpa_supplicant/aidl/sta_network.h
index 6661130..f794844 100644
--- a/wpa_supplicant/aidl/sta_network.h
+++ b/wpa_supplicant/aidl/sta_network.h
@@ -85,6 +85,8 @@
 		const std::vector<uint8_t>& in_identity) override;
 	::ndk::ScopedAStatus setEapEncryptedImsiIdentity(
 		const std::vector<uint8_t>& in_identity) override;
+	::ndk::ScopedAStatus setStrictConservativePeerMode(
+		bool in_enable) override;
 	::ndk::ScopedAStatus setEapAnonymousIdentity(
 		const std::vector<uint8_t>& in_identity) override;
 	::ndk::ScopedAStatus setEapPassword(
@@ -212,6 +214,8 @@
 		const std::vector<uint8_t>& identity);
 	ndk::ScopedAStatus setEapEncryptedImsiIdentityInternal(
 		const std::vector<uint8_t>& identity);
+	ndk::ScopedAStatus setStrictConservativePeerModeInternal(
+		bool enable);
 	ndk::ScopedAStatus setEapAnonymousIdentityInternal(
 		const std::vector<uint8_t>& identity);
 	ndk::ScopedAStatus setEapPasswordInternal(
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 065b6fb..2b1bdeb 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -2529,6 +2529,7 @@
 	{ INTe(sim_num, sim_num) },
 	{ STRe(imsi_privacy_cert, imsi_privacy_cert) },
 	{ STRe(imsi_privacy_attr, imsi_privacy_attr) },
+	{ INTe(strict_conservative_peer_mode, strict_conservative_peer_mode) },
 	{ STRe(openssl_ciphers, openssl_ciphers) },
 	{ INTe(erp, erp) },
 #endif /* IEEE8021X_EAPOL */
@@ -3953,6 +3954,11 @@
 		return 0;
 	}
 
+	if (os_strcmp(var, "strict_conservative_peer_mode") == 0) {
+		cred->strict_conservative_peer_mode = atoi(val);
+		return 0;
+	}
+
 	if (line) {
 		wpa_printf(MSG_ERROR, "Line %d: unknown cred field '%s'.",
 			   line, var);
@@ -4109,6 +4115,9 @@
 	if (os_strcmp(var, "imsi_privacy_attr") == 0)
 		return alloc_strdup(cred->imsi_privacy_attr);
 
+	if (os_strcmp(var, "strict_conservative_peer_mode") == 0)
+		return alloc_int_str(cred->strict_conservative_peer_mode);
+
 	if (os_strcmp(var, "milenage") == 0) {
 		if (!(cred->milenage))
 			return NULL;
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index a81e9eb..0316e9b 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -202,6 +202,18 @@
 	char *imsi_privacy_attr;
 
 	/**
+	 * strict_conservative_peer_mode - Whether the strict conservative peer
+	 * mode is enabled or not
+	 *
+	 * This field is used to handle the reponse of AT_PERMANENT_ID_REQ
+	 * for EAP-SIM/AKA/AKA', in convervative peer mode, a client error would
+	 * be sent to the server, but it allows to send the permanent identity
+	 * in some special cases according to 4.6.2 of RFC 4187; With the strict
+	 * mode, it never send the permanent identity to server for privacy concern.
+	 */
+	int strict_conservative_peer_mode;
+
+	/**
 	 * engine - Use an engine for private key operations
 	 */
 	int engine;
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 260df8f..372a57b 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -1044,6 +1044,9 @@
 	if (cred->imsi_privacy_attr)
 		fprintf(f, "\timsi_privacy_attr=\"%s\"\n",
 			cred->imsi_privacy_attr);
+	if (cred->strict_conservative_peer_mode)
+		fprintf(f,"\tstrict_conservative_peer_mode=\"%d\"\n",
+			cred->strict_conservative_peer_mode);
 }
 
 
diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c
index 55dcd70..593da3a 100644
--- a/wpa_supplicant/dpp_supplicant.c
+++ b/wpa_supplicant/dpp_supplicant.c
@@ -419,12 +419,21 @@
 		DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
 		MAC2STR(auth->peer_mac_addr), auth->curr_freq,
 		DPP_PA_CONNECTION_STATUS_RESULT);
-	offchannel_send_action(wpa_s, auth->curr_freq,
-			       auth->peer_mac_addr, wpa_s->own_addr, broadcast,
-			       wpabuf_head(msg), wpabuf_len(msg),
-			       500, wpas_dpp_tx_status, 0);
+	if (offchannel_send_action(wpa_s, auth->curr_freq,
+				   auth->peer_mac_addr, wpa_s->own_addr, broadcast,
+				   wpabuf_head(msg), wpabuf_len(msg),
+				   500, wpas_dpp_tx_status, 0) < 0) {
+		wpas_notify_dpp_connection_status_sent(wpa_s, result);
+		wpabuf_free(msg);
+		dpp_auth_deinit(wpa_s->dpp_auth);
+		wpa_s->dpp_auth = NULL;
+		return;
+	}
+
 	wpabuf_free(msg);
 
+	auth->conn_result_status = result;
+	auth->tx_conn_status_result_started = 1;
 	/* This exchange will be terminated in the TX status handler */
 	auth->remove_on_tx_status = 1;
 
@@ -491,6 +500,9 @@
 #endif /* CONFIG_DPP2 */
 
 	if (wpa_s->dpp_auth->remove_on_tx_status) {
+		if (auth->tx_conn_status_result_started) {
+			wpas_notify_dpp_connection_status_sent(wpa_s, auth->conn_result_status);
+		}
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Terminate authentication exchange due to a request to do so on TX status");
 		eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL);
@@ -1476,7 +1488,7 @@
 
 	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_NETWORK_ID "%d", ssid->id);
 
-	wpas_notify_dpp_config_received(wpa_s, ssid);
+	wpas_notify_dpp_config_received(wpa_s, ssid, auth->conn_status_requested ? 1 : 0);
 
 	if (wpa_s->conf->dpp_config_processing == 2)
 		ssid->disabled = 0;
@@ -1829,7 +1841,7 @@
 		wpabuf_free(msg);
 
 		/* This exchange will be terminated in the TX status handler */
-		if (wpa_s->conf->dpp_config_processing < 2 ||
+		if (wpa_s->conf->dpp_config_processing < 1 ||
 		    wpa_s->dpp_conf_backup_received)
 			auth->remove_on_tx_status = 1;
 		return;
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index 6198bd7..acd9044 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -1077,6 +1077,12 @@
 			goto fail;
 	}
 
+	if (cred->strict_conservative_peer_mode) {
+		if (wpa_config_set_quoted(ssid, "strict_conservative_peer_mode",
+					  "1") < 0)
+			goto fail;
+	}
+
 	wpa_s->next_ssid = ssid;
 	wpa_config_update_prio_list(wpa_s->conf);
 	if (!only_add)
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index a544c94..7e6d042 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -280,6 +280,12 @@
 }
 
 
+void wpas_notify_permanent_id_req_denied(struct wpa_supplicant *wpa_s)
+{
+	wpas_aidl_notify_permanent_id_req_denied(wpa_s);
+}
+
+
 void wpas_notify_scanning(struct wpa_supplicant *wpa_s)
 {
 	if (wpa_s->p2p_mgmt)
@@ -1103,13 +1109,13 @@
 /* DPP Success notifications */
 
 void wpas_notify_dpp_config_received(struct wpa_supplicant *wpa_s,
-	    struct wpa_ssid *ssid)
+	    struct wpa_ssid *ssid, bool conn_status_requested)
 {
 #ifdef CONFIG_DPP
 	if (!wpa_s)
 		return;
 
-	wpas_aidl_notify_dpp_config_received(wpa_s, ssid);
+	wpas_aidl_notify_dpp_config_received(wpa_s, ssid, conn_status_requested);
 #endif /* CONFIG_DPP */
 }
 
@@ -1123,6 +1129,17 @@
 #endif /* CONFIG_DPP */
 }
 
+void wpas_notify_dpp_connection_status_sent(struct wpa_supplicant *wpa_s,
+	    enum dpp_status_error result)
+{
+#ifdef CONFIG_DPP2
+	if (!wpa_s)
+		return;
+
+	wpas_aidl_notify_dpp_connection_status_sent(wpa_s, result);
+#endif /* CONFIG_DPP2 */
+}
+
 /* DPP Progress notifications */
 void wpas_notify_dpp_auth_success(struct wpa_supplicant *wpa_s)
 {
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index 996be84..e1b9f17 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -48,6 +48,7 @@
 				 struct wpa_ssid *ssid,
 				 enum wpa_ctrl_req_type rtype,
 				 const char *default_txt);
+void wpas_notify_permanent_id_req_denied(struct wpa_supplicant *wpa_s);
 void wpas_notify_scanning(struct wpa_supplicant *wpa_s);
 void wpas_notify_scan_done(struct wpa_supplicant *wpa_s, int success);
 void wpas_notify_scan_results(struct wpa_supplicant *wpa_s);
@@ -180,8 +181,10 @@
 void wpas_notify_hs20_rx_terms_and_conditions_acceptance(
 		struct wpa_supplicant *wpa_s, const char *url);
 void wpas_notify_dpp_config_received(struct wpa_supplicant *wpa_s,
-	    struct wpa_ssid *ssid);
+		struct wpa_ssid *ssid, bool conn_status_requested);
 void wpas_notify_dpp_config_sent(struct wpa_supplicant *wpa_s);
+void wpas_notify_dpp_connection_status_sent(struct wpa_supplicant *wpa_s,
+		enum dpp_status_error result);
 void wpas_notify_dpp_auth_success(struct wpa_supplicant *wpa_s);
 void wpas_notify_dpp_resp_pending(struct wpa_supplicant *wpa_s);
 void wpas_notify_dpp_not_compatible(struct wpa_supplicant *wpa_s);
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 8262eef..b6a1a74 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -1175,6 +1175,14 @@
 #	unencrypted identity with EAP types that support different tunnelled
 #	identity, e.g., EAP-TTLS). This field can also be used with
 #	EAP-SIM/AKA/AKA' to store the pseudonym identity.
+# strict_conservative_peer_mode: Whether the strict conservative peer mode
+#	is enabled. This field is used to handle the reponse of AT_PERMANENT_ID_REQ
+#	for EAP-SIM/AKA/AKA'. In non-strict convervative peer mode, a client
+#	error would be sent to the server, but the mode will send the permanent
+#	identity in some special cases according to 4.6.2 of RFC 4187; With the
+#	strict mode, the permanent identity is never sent to the server.
+#	0 = disabled (default)
+#	1 = enabled
 # password: Password string for EAP. This field can include either the
 #	plaintext password (using ASCII or hex string) or a NtPasswordHash
 #	(16-byte MD4 hash of password) in hash:<32 hex digits> format.
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index e13ea8f..a733ae3 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -1093,6 +1093,12 @@
 	wpa_drv_set_supp_port(wpa_s, authorized);
 }
 
+static void wpa_supplicant_permanent_id_req_denied_cb(void *ctx)
+{
+	struct wpas_supplicant *wpa_s = ctx;
+
+	wpas_notify_permanent_id_req_denied(wpa_s);
+}
 
 static void wpa_supplicant_cert_cb(void *ctx, struct tls_cert_data *cert,
 				   const char *cert_hash)
@@ -1238,6 +1244,7 @@
 	ctx->port_cb = wpa_supplicant_port_cb;
 	ctx->cb = wpa_supplicant_eapol_cb;
 	ctx->cert_cb = wpa_supplicant_cert_cb;
+	ctx->permanent_id_req_denied_cb = wpa_supplicant_permanent_id_req_denied_cb;
 	ctx->cert_in_cb = wpa_s->conf->cert_in_cb;
 	ctx->status_cb = wpa_supplicant_status_cb;
 	ctx->eap_error_cb = wpa_supplicant_eap_error_cb;