Merge changes from topic "propagate_eap_failure" into pi-dev

* changes:
  Report the EAP method failure up to the framework
  Propagate the EAP method error code
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index 0043707..11d8129 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -94,6 +94,12 @@
 		sm->eapol_cb->notify_status(sm->eapol_ctx, status, parameter);
 }
 
+static void eap_report_error(struct eap_sm *sm, int error_code)
+{
+	wpa_printf(MSG_DEBUG, "EAP: Error notification: %d", error_code);
+	if (sm->eapol_cb->notify_eap_error)
+		sm->eapol_cb->notify_eap_error(sm->eapol_ctx, error_code);
+}
 
 static void eap_sm_free_key(struct eap_sm *sm)
 {
@@ -1934,6 +1940,7 @@
 	const struct eap_hdr *hdr;
 	size_t plen;
 	const u8 *pos;
+        int error_code;
 
 	sm->rxReq = sm->rxResp = sm->rxSuccess = sm->rxFailure = FALSE;
 	sm->reqId = 0;
@@ -2018,6 +2025,13 @@
 	case EAP_CODE_FAILURE:
 		wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure");
 		eap_notify_status(sm, "completion", "failure");
+
+		/* Get the error code from method */
+		if (sm->m->get_error_code) {
+			error_code = sm->m->get_error_code(sm->eap_method_priv);
+			if (error_code != NO_EAP_METHOD_ERROR)
+				eap_report_error(sm, error_code);
+		}
 		sm->rxFailure = TRUE;
 		break;
 	case EAP_CODE_INITIATE:
diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h
index b5591a0..d0837e3 100644
--- a/src/eap_peer/eap.h
+++ b/src/eap_peer/eap.h
@@ -246,6 +246,13 @@
 	void (*notify_status)(void *ctx, const char *status,
 			      const char *parameter);
 
+	/**
+	 * notify_eap_error - Report EAP method error code
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * @error_code: Error code from the used EAP method
+	 */
+	void (*notify_eap_error)(void *ctx, int error_code);
+
 #ifdef CONFIG_EAP_PROXY
 	/**
 	 * eap_proxy_cb - Callback signifying any updates from eap_proxy
diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c
index 7a6bfc9..679f101 100644
--- a/src/eap_peer/eap_aka.c
+++ b/src/eap_peer/eap_aka.c
@@ -56,6 +56,7 @@
 	int kdf_negotiation;
 	u16 last_kdf_attrs[EAP_AKA_PRIME_KDF_MAX];
 	size_t last_kdf_count;
+	int error_code;
 };
 
 
@@ -99,6 +100,9 @@
 
 	data->eap_method = EAP_TYPE_AKA;
 
+	/* Zero is a valid error code, so we need to initialize */
+	data->error_code = NO_EAP_METHOD_ERROR;
+
 	eap_aka_state(data, CONTINUE);
 	data->prev_id = -1;
 
@@ -1180,6 +1184,7 @@
 
 	eap_sim_report_notification(sm->msg_ctx, attr->notification, 1);
 	if (attr->notification >= 0 && attr->notification < 32768) {
+		data->error_code = attr->notification;
 		eap_aka_state(data, FAILURE);
 	} else if (attr->notification == EAP_SIM_SUCCESS &&
 		   data->state == RESULT_SUCCESS)
@@ -1523,6 +1528,20 @@
 	return key;
 }
 
+static int eap_aka_get_error_code(void *priv)
+{
+	struct eap_aka_data *data = priv;
+
+	if (!data)
+		return NO_EAP_METHOD_ERROR;
+
+	int current_data_error = data->error_code;
+
+	/* Now reset for next transaction */
+	data->error_code = NO_EAP_METHOD_ERROR;
+
+	return current_data_error;
+}
 
 int eap_peer_aka_register(void)
 {
@@ -1544,6 +1563,7 @@
 	eap->init_for_reauth = eap_aka_init_for_reauth;
 	eap->get_identity = eap_aka_get_identity;
 	eap->get_emsk = eap_aka_get_emsk;
+	eap->get_error_code = eap_aka_get_error_code;
 
 	return eap_peer_method_register(eap);
 }
@@ -1571,6 +1591,7 @@
 	eap->init_for_reauth = eap_aka_init_for_reauth;
 	eap->get_identity = eap_aka_get_identity;
 	eap->get_emsk = eap_aka_get_emsk;
+	eap->get_error_code = eap_aka_get_error_code;
 
 	return eap_peer_method_register(eap);
 }
diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h
index 6ab2483..5b38969 100644
--- a/src/eap_peer/eap_i.h
+++ b/src/eap_peer/eap_i.h
@@ -14,6 +14,8 @@
 #include "eap_peer/eap.h"
 #include "eap_common/eap_common.h"
 
+#define NO_EAP_METHOD_ERROR (-1)
+
 /* RFC 4137 - EAP Peer state machine */
 
 typedef enum {
@@ -206,6 +208,17 @@
 	const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len);
 
 	/**
+	 * get_error_code - Get latest EAP method error code
+	 * @priv: Pointer to private EAP method data from eap_method::init()
+	 * Returns: An int for the EAP Method Error code if exists or
+	 * NO_EAP_METHOD_ERROR otherwise
+	 *
+	 * This method is an optional handler that only EAP methods that need to
+	 * report their error code need to implement.
+	 */
+	int (*get_error_code)(void *priv);
+
+	/**
 	 * free - Free EAP method data
 	 * @method: Pointer to the method data registered with
 	 * eap_peer_method_register().
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
index cd687cb..c0896aa 100644
--- a/src/eap_peer/eap_sim.c
+++ b/src/eap_peer/eap_sim.c
@@ -47,6 +47,7 @@
 	} state;
 	int result_ind, use_result_ind;
 	int use_pseudonym;
+	int error_code;
 };
 
 
@@ -94,6 +95,9 @@
 		return NULL;
 	}
 
+	/* Zero is a valid error code, so we need to initialize */
+	data->error_code = NO_EAP_METHOD_ERROR;
+
 	data->min_num_chal = 2;
 	if (config && config->phase1) {
 		char *pos = os_strstr(config->phase1, "sim_min_num_chal=");
@@ -920,6 +924,7 @@
 
 	eap_sim_report_notification(sm->msg_ctx, attr->notification, 0);
 	if (attr->notification >= 0 && attr->notification < 32768) {
+		data->error_code = attr->notification;
 		eap_sim_state(data, FAILURE);
 	} else if (attr->notification == EAP_SIM_SUCCESS &&
 		   data->state == RESULT_SUCCESS)
@@ -1243,6 +1248,20 @@
 	return key;
 }
 
+static int eap_sim_get_error_code(void *priv)
+{
+	struct eap_sim_data *data = priv;
+
+	if (!data)
+		return NO_EAP_METHOD_ERROR;
+
+	int current_data_error = data->error_code;
+
+	/* Now reset for next transaction */
+	data->error_code = NO_EAP_METHOD_ERROR;
+
+	return current_data_error;
+}
 
 int eap_peer_sim_register(void)
 {
@@ -1264,6 +1283,7 @@
 	eap->init_for_reauth = eap_sim_init_for_reauth;
 	eap->get_identity = eap_sim_get_identity;
 	eap->get_emsk = eap_sim_get_emsk;
+	eap->get_error_code = eap_sim_get_error_code;
 
 	return eap_peer_method_register(eap);
 }
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index 8e4f0e4..cb463ef 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -2014,6 +2014,13 @@
 		sm->ctx->status_cb(sm->ctx->ctx, status, parameter);
 }
 
+static void eapol_sm_notify_eap_error(void *ctx, int error_code)
+{
+	struct eapol_sm *sm = ctx;
+
+	if (sm->ctx->eap_error_cb)
+		sm->ctx->eap_error_cb(sm->ctx->ctx, error_code);
+}
 
 #ifdef CONFIG_EAP_PROXY
 
@@ -2062,6 +2069,7 @@
 	eapol_sm_eap_param_needed,
 	eapol_sm_notify_cert,
 	eapol_sm_notify_status,
+	eapol_sm_notify_eap_error,
 #ifdef CONFIG_EAP_PROXY
 	eapol_sm_eap_proxy_cb,
 	eapol_sm_eap_proxy_notify_sim_status,
diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h
index a25c799..74f40bb 100644
--- a/src/eapol_supp/eapol_supp_sm.h
+++ b/src/eapol_supp/eapol_supp_sm.h
@@ -271,6 +271,13 @@
 	void (*status_cb)(void *ctx, const char *status,
 			  const char *parameter);
 
+	/**
+	 * eap_error_cb - Notification of EAP method error
+	 * @ctx: Callback context (ctx)
+	 * @error_code: EAP method error code
+	 */
+	void (*eap_error_cb)(void *ctx, int error_code);
+
 #ifdef CONFIG_EAP_PROXY
 	/**
 	 * eap_proxy_cb - Callback signifying any updates from eap_proxy
diff --git a/wpa_supplicant/hidl/1.1/hidl.cpp b/wpa_supplicant/hidl/1.1/hidl.cpp
index 10fc36c..95194af 100644
--- a/wpa_supplicant/hidl/1.1/hidl.cpp
+++ b/wpa_supplicant/hidl/1.1/hidl.cpp
@@ -625,3 +625,21 @@
 
 	hidl_manager->notifyApStaDeauthorized(wpa_s, sta, p2p_dev_addr);
 }
+
+void wpas_hidl_notify_eap_error(
+    struct wpa_supplicant *wpa_s, int error_code)
+{
+	if (!wpa_s)
+		return;
+
+	wpa_printf(
+	    MSG_DEBUG,
+            "Notifying EAP Error: %d ", error_code);
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyEapError(wpa_s, error_code);
+}
+
diff --git a/wpa_supplicant/hidl/1.1/hidl.h b/wpa_supplicant/hidl/1.1/hidl.h
index 96631f2..1dfadc6 100644
--- a/wpa_supplicant/hidl/1.1/hidl.h
+++ b/wpa_supplicant/hidl/1.1/hidl.h
@@ -91,6 +91,7 @@
     struct wpa_supplicant *wpa_s, const u8 *sta, const u8 *p2p_dev_addr);
 void wpas_hidl_notify_ap_sta_deauthorized(
     struct wpa_supplicant *wpa_s, const u8 *sta, const u8 *p2p_dev_addr);
+void wpas_hidl_notify_eap_error(struct wpa_supplicant *wpa_s, int error_code);
 #else   // CONFIG_CTRL_IFACE_HIDL
 static inline int wpas_hidl_register_interface(struct wpa_supplicant *wpa_s)
 {
@@ -212,6 +213,10 @@
     struct wpa_supplicant *wpa_s, const u8 *sta, const u8 *p2p_dev_addr)
 {
 }
+static void wpas_hidl_notify_eap_error(
+    struct wpa_supplicant *wpa_s, int error_code)
+{
+}
 #endif  // CONFIG_CTRL_IFACE_HIDL
 
 #ifdef _cplusplus
diff --git a/wpa_supplicant/hidl/1.1/hidl_manager.cpp b/wpa_supplicant/hidl/1.1/hidl_manager.cpp
index 5d09a22..ef239c1 100644
--- a/wpa_supplicant/hidl/1.1/hidl_manager.cpp
+++ b/wpa_supplicant/hidl/1.1/hidl_manager.cpp
@@ -1380,6 +1380,32 @@
 		std::placeholders::_1, id));
 }
 
+void HidlManager::notifyEapError(struct wpa_supplicant *wpa_s, int error_code)
+{
+	typedef ISupplicantStaIfaceCallback::EapErrorCode EapErrorCode;
+
+	if (!wpa_s)
+		return;
+
+	switch (static_cast<EapErrorCode>(error_code)) {
+		case EapErrorCode::SIM_GENERAL_FAILURE_AFTER_AUTH:
+		case EapErrorCode::SIM_TEMPORARILY_DENIED:
+		case EapErrorCode::SIM_NOT_SUBSCRIBED:
+		case EapErrorCode::SIM_GENERAL_FAILURE_BEFORE_AUTH:
+		case EapErrorCode::SIM_VENDOR_SPECIFIC_EXPIRED_CERT:
+			break;
+		default:
+			return;
+	}
+
+	callWithEachStaIfaceCallback(
+	    wpa_s->ifname,
+	    std::bind(
+		&ISupplicantStaIfaceCallback::onEapFailure_1_1,
+		std::placeholders::_1,
+		static_cast<EapErrorCode>(error_code)));
+}
+
 /**
  * Retrieve the |ISupplicantP2pIface| hidl object reference using the provided
  * ifname.
diff --git a/wpa_supplicant/hidl/1.1/hidl_manager.h b/wpa_supplicant/hidl/1.1/hidl_manager.h
index 821011b..b596a38 100644
--- a/wpa_supplicant/hidl/1.1/hidl_manager.h
+++ b/wpa_supplicant/hidl/1.1/hidl_manager.h
@@ -16,7 +16,7 @@
 #include <android/hardware/wifi/supplicant/1.0/ISupplicantCallback.h>
 #include <android/hardware/wifi/supplicant/1.0/ISupplicantP2pIfaceCallback.h>
 #include <android/hardware/wifi/supplicant/1.0/ISupplicantP2pNetworkCallback.h>
-#include <android/hardware/wifi/supplicant/1.0/ISupplicantStaIfaceCallback.h>
+#include <android/hardware/wifi/supplicant/1.1/ISupplicantStaIfaceCallback.h>
 #include <android/hardware/wifi/supplicant/1.0/ISupplicantStaNetworkCallback.h>
 
 #include "p2p_iface.h"
@@ -122,6 +122,8 @@
 	void notifyApStaDeauthorized(
 	    struct wpa_supplicant *wpa_s, const u8 *sta,
 	    const u8 *p2p_dev_addr);
+	void notifyEapError(
+	    struct wpa_supplicant *wpa_s, int error_code);
 
 	// Methods called from hidl objects.
 	void notifyExtRadioWorkStart(struct wpa_supplicant *wpa_s, uint32_t id);
diff --git a/wpa_supplicant/hidl/1.1/sta_iface.cpp b/wpa_supplicant/hidl/1.1/sta_iface.cpp
index 08b484c..54c4007 100644
--- a/wpa_supplicant/hidl/1.1/sta_iface.cpp
+++ b/wpa_supplicant/hidl/1.1/sta_iface.cpp
@@ -22,7 +22,7 @@
 }
 
 namespace {
-using android::hardware::wifi::supplicant::V1_0::ISupplicantStaIface;
+using android::hardware::wifi::supplicant::V1_1::ISupplicantStaIface;
 using android::hardware::wifi::supplicant::V1_0::SupplicantStatus;
 using android::hardware::wifi::supplicant::V1_0::SupplicantStatusCode;
 using android::hardware::wifi::supplicant::V1_1::implementation::HidlManager;
@@ -215,12 +215,21 @@
 }
 
 Return<void> StaIface::registerCallback(
+    const sp<android::hardware::wifi::supplicant::V1_0::ISupplicantStaIfaceCallback>
+    & callback, registerCallback_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::registerCallbackInternal, _hidl_cb, callback);
+}
+
+Return<void> StaIface::registerCallback_1_1(
     const sp<ISupplicantStaIfaceCallback> &callback,
     registerCallback_cb _hidl_cb)
 {
 	return validateAndCall(
 	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
-	    &StaIface::registerCallbackInternal, _hidl_cb, callback);
+	    &StaIface::registerCallbackInternal_1_1, _hidl_cb, callback);
 }
 
 Return<void> StaIface::reassociate(reassociate_cb _hidl_cb)
@@ -570,6 +579,12 @@
 }
 
 SupplicantStatus StaIface::registerCallbackInternal(
+    const sp<android::hardware::wifi::supplicant::V1_0::ISupplicantStaIfaceCallback> &callback)
+{
+	return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+}
+
+SupplicantStatus StaIface::registerCallbackInternal_1_1(
     const sp<ISupplicantStaIfaceCallback> &callback)
 {
 	HidlManager *hidl_manager = HidlManager::getInstance();
diff --git a/wpa_supplicant/hidl/1.1/sta_iface.h b/wpa_supplicant/hidl/1.1/sta_iface.h
index 8288ce2..1f1e64b 100644
--- a/wpa_supplicant/hidl/1.1/sta_iface.h
+++ b/wpa_supplicant/hidl/1.1/sta_iface.h
@@ -15,8 +15,8 @@
 
 #include <android-base/macros.h>
 
-#include <android/hardware/wifi/supplicant/1.0/ISupplicantStaIface.h>
-#include <android/hardware/wifi/supplicant/1.0/ISupplicantStaIfaceCallback.h>
+#include <android/hardware/wifi/supplicant/1.1/ISupplicantStaIface.h>
+#include <android/hardware/wifi/supplicant/1.1/ISupplicantStaIfaceCallback.h>
 #include <android/hardware/wifi/supplicant/1.0/ISupplicantStaNetwork.h>
 
 extern "C" {
@@ -71,6 +71,9 @@
 	    SupplicantNetworkId id, getNetwork_cb _hidl_cb) override;
 	Return<void> listNetworks(listNetworks_cb _hidl_cb) override;
 	Return<void> registerCallback(
+	    const sp<android::hardware::wifi::supplicant::V1_0::ISupplicantStaIfaceCallback>
+	    & callback, registerCallback_cb _hidl_cb) override;
+	Return<void> registerCallback_1_1(
 	    const sp<ISupplicantStaIfaceCallback>& callback,
 	    registerCallback_cb _hidl_cb) override;
 	Return<void> reassociate(reassociate_cb _hidl_cb) override;
@@ -168,6 +171,9 @@
 	std::pair<SupplicantStatus, std::vector<SupplicantNetworkId>>
 	listNetworksInternal();
 	SupplicantStatus registerCallbackInternal(
+	    const sp<android::hardware::wifi::supplicant::V1_0::ISupplicantStaIfaceCallback>
+	    & callback);
+	SupplicantStatus registerCallbackInternal_1_1(
 	    const sp<ISupplicantStaIfaceCallback>& callback);
 	SupplicantStatus reassociateInternal();
 	SupplicantStatus reconnectInternal();
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index a5db82c..3832a33 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -887,6 +887,12 @@
 		     status, parameter);
 }
 
+void wpas_notify_eap_error(struct wpa_supplicant *wpa_s, int error_code)
+{
+	wpa_dbg(wpa_s, MSG_ERROR,
+		"EAP Error code = %d", error_code);
+	wpas_hidl_notify_eap_error(wpa_s, error_code);
+}
 
 void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s,
 					   struct wpa_ssid *ssid)
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index 26b07f5..c3ac3d1 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -138,6 +138,7 @@
 		      const u8 *ie, size_t ie_len, u32 ssi_signal);
 void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status,
 			    const char *parameter);
+void wpas_notify_eap_error(struct wpa_supplicant *wpa_s, int error_code);
 void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s,
 					   struct wpa_ssid *ssid);
 void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s,
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index e44f6af..e29f13b 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -1037,6 +1037,12 @@
 	wpas_notify_eap_status(wpa_s, status, parameter);
 }
 
+static void wpa_supplicant_eap_error_cb(void *ctx, int error_code)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	wpas_notify_eap_error(wpa_s, error_code);
+}
 
 static void wpa_supplicant_set_anon_id(void *ctx, const u8 *id, size_t len)
 {
@@ -1115,6 +1121,7 @@
 	ctx->cert_cb = wpa_supplicant_cert_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;
 	ctx->set_anon_id = wpa_supplicant_set_anon_id;
 	ctx->cb_ctx = wpa_s;
 	wpa_s->eapol = eapol_sm_init(ctx);