Merge "Do not do NAT detection when using IPv6 address"
diff --git a/Android.bp b/Android.bp
index f954c1c..01c0762 100644
--- a/Android.bp
+++ b/Android.bp
@@ -14,8 +14,11 @@
java_sdk_library {
name: "android.net.ipsec.ike",
+ defaults: ["framework-module-defaults"],
installable: true,
- sdk_version: "module_current",
+
+ // ike is used as a shared library.
+ shared_library: true,
aidl: {
local_include_dirs: ["src/java"],
@@ -28,7 +31,6 @@
libs: [
"unsupportedappusage",
- "framework-annotations-lib",
],
api_packages: [
@@ -41,13 +43,13 @@
// being overwritten by the frameworks class copies.
jarjar_rules: "jarjar-rules-shared.txt",
- plugins: ["java_api_finder"],
-
hostdex: true, // for hiddenapi check
apex_available: [
"com.android.ipsec",
"test_com.android.ipsec",
],
+
+ stubs_library_visibility: ["//visibility:public"],
}
filegroup {
@@ -86,32 +88,3 @@
// being overwritten by the frameworks class copies.
jarjar_rules: "jarjar-rules-shared.txt",
}
-
-stubs_defaults {
- name: "ike-stubs-defaults",
- srcs: [":ike-api-srcs"],
-}
-
-droidstubs {
- name: "android.net.ipsec.ike.api.sources.module_libs_api",
- defaults: [
- "framework-module-api-defaults-module_libs_api",
- "ike-stubs-defaults",
- ],
- dist: { dest: "android.net.ipsec.ike.txt" },
-}
-
-droidstubs {
- name: "android.net.ipsec.ike.stubs.sources.module_libs_api",
- defaults: [
- "framework-module-stubs-defaults-module_libs_api",
- "ike-stubs-defaults",
- ],
-}
-
-java_library {
- name: "android.net.ipsec.ike.stubs.module_libs_api",
- srcs: [":android.net.ipsec.ike.stubs.sources.module_libs_api"],
- defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
- dist: { dest: "android.net.ipsec.ike.jar" },
-}
diff --git a/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachine.java b/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachine.java
index c92f465..fd500b8 100644
--- a/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachine.java
+++ b/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachine.java
@@ -773,9 +773,12 @@
// TODO: Initiate deletion
mChildSmCallback.onChildSaDeleted(createChildResult.respSpi.getSpi());
+ handleChildFatalError(e);
+ } finally {
+ // In the successful case the transform in ChildSaRecord has taken ownership
+ // of the SPI (in IpSecService), and will keep it alive.
createChildResult.initSpi.close();
createChildResult.respSpi.close();
- handleChildFatalError(e);
}
break;
case CREATE_STATUS_CHILD_ERROR_INVALID_MSG:
@@ -882,19 +885,21 @@
/** Initial state of ChildSessionStateMachine. */
class Initial extends CreateChildLocalCreateBase {
+ List<IkePayload> mRequestPayloads;
+
@Override
public boolean processStateMessage(Message message) {
switch (message.what) {
case CMD_HANDLE_FIRST_CHILD_EXCHANGE:
FirstChildNegotiationData childNegotiationData =
(FirstChildNegotiationData) message.obj;
- List<IkePayload> reqPayloads = childNegotiationData.requestPayloads;
+ mRequestPayloads = childNegotiationData.requestPayloads;
List<IkePayload> respPayloads = childNegotiationData.responsePayloads;
// Negotiate Child SA. The exchangeType has been validated in
// IkeSessionStateMachine. Won't validate it again here.
validateAndBuildChild(
- reqPayloads,
+ mRequestPayloads,
respPayloads,
EXCHANGE_TYPE_IKE_AUTH,
EXCHANGE_TYPE_IKE_AUTH,
@@ -919,6 +924,11 @@
return NOT_HANDLED;
}
}
+
+ @Override
+ public void exitState() {
+ CreateChildSaHelper.releaseSpiResources(mRequestPayloads);
+ }
}
/**
@@ -974,6 +984,11 @@
return NOT_HANDLED;
}
}
+
+ @Override
+ public void exitState() {
+ CreateChildSaHelper.releaseSpiResources(mRequestPayloads);
+ }
}
/**
@@ -1355,6 +1370,9 @@
| IOException e) {
// #makeChildSaRecord failed
handleProcessRespOrSaCreationFailAndQuit(resp.registeredSpi, e);
+ } finally {
+ // In the successful case the transform in ChildSaRecord has taken
+ // ownership of the SPI (in IpSecService), and will keep it alive.
createChildResult.initSpi.close();
createChildResult.respSpi.close();
}
@@ -1396,6 +1414,11 @@
}
handleChildFatalError(exception);
}
+
+ @Override
+ public void exitState() {
+ CreateChildSaHelper.releaseSpiResources(mRequestPayloads);
+ }
}
private ChildSaProposal addDhGroupsFromChildSessionParamsIfAbsent() {
@@ -1564,12 +1587,14 @@
| SpiUnavailableException
| IOException e) {
// #makeChildSaRecord failed.
- createChildResult.initSpi.close();
- createChildResult.respSpi.close();
-
handleCreationFailureAndBackToIdle(
new NoValidProposalChosenException(
"Error in Child SA creation", e));
+ } finally {
+ // In the successful case the transform in ChildSaRecord has taken ownership
+ // of the SPI (in IpSecService), and will keep it alive.
+ createChildResult.initSpi.close();
+ createChildResult.respSpi.close();
}
break;
case CREATE_STATUS_CHILD_ERROR_INVALID_MSG:
@@ -2046,6 +2071,19 @@
return hasExpectedRekeyNotify;
}
+ public static void releaseSpiResources(List<IkePayload> reqPayloads) {
+ if (reqPayloads == null) {
+ return;
+ }
+
+ IkeSaPayload saPayload =
+ IkePayload.getPayloadForTypeInProvidedList(
+ IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class, reqPayloads);
+ if (saPayload != null) {
+ saPayload.releaseChildSpiResourcesIfExists();
+ }
+ }
+
/** Validate the received payload list and negotiate Child SA. */
private static CreateChildResult validateAndNegotiateChild(
List<IkePayload> reqPayloads,
diff --git a/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java b/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
index 3592ffb..1a3bf18 100644
--- a/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
+++ b/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
@@ -35,9 +35,14 @@
import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP;
import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP;
import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_REKEY_SA;
+import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_AUTH;
import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_CP;
import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_DELETE;
+import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_EAP;
import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_NOTIFY;
+import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_SA;
+import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_TS_INITIATOR;
+import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_TS_RESPONDER;
import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_VENDOR;
import static com.android.internal.net.ipsec.ike.utils.IkeAlarmReceiver.ACTION_DELETE_CHILD;
import static com.android.internal.net.ipsec.ike.utils.IkeAlarmReceiver.ACTION_DELETE_IKE;
@@ -127,7 +132,6 @@
import com.android.internal.net.ipsec.ike.message.IkePayload;
import com.android.internal.net.ipsec.ike.message.IkeSaPayload;
import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IkeProposal;
-import com.android.internal.net.ipsec.ike.message.IkeTsPayload;
import com.android.internal.net.ipsec.ike.message.IkeVendorPayload;
import com.android.internal.net.ipsec.ike.utils.IkeAlarmReceiver;
import com.android.internal.net.ipsec.ike.utils.IkeSecurityParameterIndex;
@@ -947,6 +951,10 @@
}
}
+ // Release IPsec SPIs if IKE Session is terminated before receiving the IKE AUTH response
+ // that contains the first child SA proposal
+ CreateChildSaHelper.releaseSpiResources(mFirstChildReqList);
+
if (mIkeNattKeepalive != null) {
mIkeNattKeepalive.stop();
}
@@ -3090,50 +3098,26 @@
protected List<IkePayload> extractChildPayloadsFromMessage(IkeMessage ikeMessage)
throws InvalidSyntaxException {
- IkeSaPayload saPayload =
- ikeMessage.getPayloadForType(IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class);
- IkeTsPayload tsInitPayload =
- ikeMessage.getPayloadForType(
- IkePayload.PAYLOAD_TYPE_TS_INITIATOR, IkeTsPayload.class);
- IkeTsPayload tsRespPayload =
- ikeMessage.getPayloadForType(
- IkePayload.PAYLOAD_TYPE_TS_RESPONDER, IkeTsPayload.class);
-
- List<IkeNotifyPayload> notifyPayloads =
- ikeMessage.getPayloadListForType(
- IkePayload.PAYLOAD_TYPE_NOTIFY, IkeNotifyPayload.class);
-
- IkeConfigPayload configPayload =
- ikeMessage.getPayloadForType(
- IkePayload.PAYLOAD_TYPE_CP, IkeConfigPayload.class);
-
- boolean hasErrorNotify = false;
List<IkePayload> list = new LinkedList<>();
- for (IkeNotifyPayload payload : notifyPayloads) {
- if (payload.isNewChildSaNotify()) {
- list.add(payload);
- if (payload.isErrorNotify()) {
- hasErrorNotify = true;
- }
+ for (IkePayload payload : ikeMessage.ikePayloadList) {
+ switch (payload.payloadType) {
+ case PAYLOAD_TYPE_SA: // fall through
+ case PAYLOAD_TYPE_TS_INITIATOR: // fall through
+ case PAYLOAD_TYPE_TS_RESPONDER: // fall through
+ case PAYLOAD_TYPE_CP:
+ list.add(payload);
+ break;
+ case PAYLOAD_TYPE_NOTIFY:
+ if (((IkeNotifyPayload) payload).isNewChildSaNotify()) {
+ list.add(payload);
+ }
+ break;
+ default:
+ // Ignore payloads unrelated with Child negotiation
}
}
- // If there is no error notification, SA, TS-initiator and TS-responder MUST all be
- // included in this message.
- if (!hasErrorNotify
- && (saPayload == null || tsInitPayload == null || tsRespPayload == null)) {
- throw new InvalidSyntaxException(
- "SA, TS-Initiator or TS-Responder payload is missing.");
- }
-
- list.add(saPayload);
- list.add(tsInitPayload);
- list.add(tsRespPayload);
-
- if (configPayload != null) {
- list.add(configPayload);
- }
-
+ // Payload validation is done in ChildSessionStateMachine
return list;
}
@@ -3214,12 +3198,11 @@
"Expected EXCHANGE_TYPE_IKE_AUTH but received: " + exchangeType);
}
+ validateIkeAuthResp(ikeMessage);
+
List<IkePayload> childReqList =
extractChildPayloadsFromMessage(mRetransmitter.getMessage());
-
if (mUseEap) {
- validateIkeAuthRespWithEapPayload(ikeMessage);
-
// childReqList needed after EAP completed, so persist to IkeSessionStateMachine
// state.
mFirstChildReqList = childReqList;
@@ -3227,12 +3210,12 @@
IkeEapPayload ikeEapPayload =
ikeMessage.getPayloadForType(
IkePayload.PAYLOAD_TYPE_EAP, IkeEapPayload.class);
-
+ if (ikeEapPayload == null) {
+ throw new AuthenticationFailedException("Missing EAP payload");
+ }
deferMessage(obtainMessage(CMD_EAP_START_EAP_AUTH, ikeEapPayload));
transitionTo(mCreateIkeLocalIkeAuthInEap);
} else {
- validateIkeAuthRespWithChildPayloads(ikeMessage);
-
notifyIkeSessionSetup(ikeMessage);
performFirstChildNegotiation(
@@ -3345,40 +3328,12 @@
return buildIkeAuthReqMessage(payloadList);
}
- private void validateIkeAuthRespWithEapPayload(IkeMessage respMsg)
- throws IkeProtocolException {
- IkeEapPayload ikeEapPayload =
- respMsg.getPayloadForType(IkePayload.PAYLOAD_TYPE_EAP, IkeEapPayload.class);
- if (ikeEapPayload == null) {
- throw new AuthenticationFailedException("Missing EAP payload");
- }
-
- // TODO: check that we don't receive any ChildSaRespPayloads here
-
- List<IkePayload> nonEapPayloads = new LinkedList<>();
- nonEapPayloads.addAll(respMsg.ikePayloadList);
- nonEapPayloads.remove(ikeEapPayload);
- validateIkeAuthResp(nonEapPayloads);
- }
-
- private void validateIkeAuthRespWithChildPayloads(IkeMessage respMsg)
- throws IkeProtocolException {
- // Extract and validate existence of payloads for first Child SA setup.
- List<IkePayload> childSaRespPayloads = extractChildPayloadsFromMessage(respMsg);
-
- List<IkePayload> nonChildPayloads = new LinkedList<>();
- nonChildPayloads.addAll(respMsg.ikePayloadList);
- nonChildPayloads.removeAll(childSaRespPayloads);
-
- validateIkeAuthResp(nonChildPayloads);
- }
-
- private void validateIkeAuthResp(List<IkePayload> payloadList) throws IkeProtocolException {
+ private void validateIkeAuthResp(IkeMessage authResp) throws IkeProtocolException {
// Validate IKE Authentication
IkeAuthPayload authPayload = null;
List<IkeCertPayload> certPayloads = new LinkedList<>();
- for (IkePayload payload : payloadList) {
+ for (IkePayload payload : authResp.ikePayloadList) {
switch (payload.payloadType) {
case IkePayload.PAYLOAD_TYPE_ID_RESPONDER:
mRespIdPayload = (IkeIdPayload) payload;
@@ -3400,7 +3355,18 @@
case IkePayload.PAYLOAD_TYPE_NOTIFY:
IkeNotifyPayload notifyPayload = (IkeNotifyPayload) payload;
if (notifyPayload.isErrorNotify()) {
- throw notifyPayload.validateAndBuildIkeException();
+ if (notifyPayload.isNewChildSaNotify()
+ && authResp.getPayloadForType(
+ PAYLOAD_TYPE_AUTH, IkeAuthPayload.class)
+ != null) {
+ // If error is for creating Child and Auth payload is included, try
+ // to do authentication first and let ChildSessionStateMachine
+ // handle the error later.
+ continue;
+ } else {
+ throw notifyPayload.validateAndBuildIkeException();
+ }
+
} else {
// Unknown and unexpected status notifications are ignored as per
// RFC7296.
@@ -3410,6 +3376,12 @@
+ notifyPayload.notifyType);
}
break;
+ case PAYLOAD_TYPE_SA: // Will be handled separately; fall through
+ case PAYLOAD_TYPE_CP: // Will be handled separately; fall through
+ case PAYLOAD_TYPE_TS_INITIATOR: // Will be handled separately; fall through
+ case PAYLOAD_TYPE_TS_RESPONDER: // Will be handled separately; fall through
+ case PAYLOAD_TYPE_EAP: // Will be handled separately
+ break;
default:
logw(
"Received unexpected payload in IKE AUTH response. Payload"
@@ -3695,18 +3667,11 @@
"Expected EXCHANGE_TYPE_IKE_AUTH but received: " + exchangeType);
}
- // Extract and validate existence of payloads for first Child SA setup.
- List<IkePayload> childSaRespPayloads = extractChildPayloadsFromMessage(ikeMessage);
-
- List<IkePayload> nonChildPayloads = new LinkedList<>();
- nonChildPayloads.addAll(ikeMessage.ikePayloadList);
- nonChildPayloads.removeAll(childSaRespPayloads);
-
- validateIkeAuthRespPostEap(nonChildPayloads);
-
+ validateIkeAuthRespPostEap(ikeMessage);
notifyIkeSessionSetup(ikeMessage);
- performFirstChildNegotiation(mFirstChildReqList, childSaRespPayloads);
+ performFirstChildNegotiation(
+ mFirstChildReqList, extractChildPayloadsFromMessage(ikeMessage));
} catch (IkeProtocolException e) {
// Notify the remote because they may have set up the IKE SA.
sendEncryptedIkeMessage(buildIkeDeleteReq(mCurrentIkeSaRecord));
@@ -3723,11 +3688,10 @@
handleIkeFatalError(ikeException);
}
- private void validateIkeAuthRespPostEap(List<IkePayload> payloadList)
- throws IkeProtocolException {
+ private void validateIkeAuthRespPostEap(IkeMessage authResp) throws IkeProtocolException {
IkeAuthPayload authPayload = null;
- for (IkePayload payload : payloadList) {
+ for (IkePayload payload : authResp.ikePayloadList) {
switch (payload.payloadType) {
case IkePayload.PAYLOAD_TYPE_AUTH:
authPayload = (IkeAuthPayload) payload;
@@ -3735,7 +3699,18 @@
case IkePayload.PAYLOAD_TYPE_NOTIFY:
IkeNotifyPayload notifyPayload = (IkeNotifyPayload) payload;
if (notifyPayload.isErrorNotify()) {
- throw notifyPayload.validateAndBuildIkeException();
+ if (notifyPayload.isNewChildSaNotify()
+ && authResp.getPayloadForType(
+ PAYLOAD_TYPE_AUTH, IkeAuthPayload.class)
+ != null) {
+ // If error is for creating Child and Auth payload is included, try
+ // to do authentication first and let ChildSessionStateMachine
+ // handle the error later.
+ continue;
+ } else {
+ throw notifyPayload.validateAndBuildIkeException();
+ }
+
} else {
// Unknown and unexpected status notifications are ignored as per
// RFC7296.
@@ -3745,6 +3720,11 @@
+ notifyPayload.notifyType);
}
break;
+ case PAYLOAD_TYPE_SA: // Will be handled separately; fall through
+ case PAYLOAD_TYPE_CP: // Will be handled separately; fall through
+ case PAYLOAD_TYPE_TS_INITIATOR: // Will be handled separately; fall through
+ case PAYLOAD_TYPE_TS_RESPONDER: // Will be handled separately; fall through
+ break;
default:
logw(
"Received unexpected payload in IKE AUTH response. Payload"
diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeMessage.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeMessage.java
index 1c5f1f6..3da649a 100644
--- a/src/java/com/android/internal/net/ipsec/ike/message/IkeMessage.java
+++ b/src/java/com/android/internal/net/ipsec/ike/message/IkeMessage.java
@@ -503,10 +503,12 @@
int fragNum = i + 1; // 1-based
+ int fragFirstInnerPayload =
+ i == 0 ? firstInnerPayload : IkePayload.PAYLOAD_TYPE_NO_NEXT;
IkeSkfPayload skfPayload =
new IkeSkfPayload(
- ikeHeader,
- firstInnerPayload,
+ skfHeader,
+ fragFirstInnerPayload,
unencryptedData,
integrityMac,
encryptCipher,
@@ -515,11 +517,7 @@
fragNum,
totalFragments);
- packetList[i] =
- encodeHeaderAndBody(
- skfHeader,
- skfPayload,
- i == 0 ? firstInnerPayload : IkePayload.PAYLOAD_TYPE_NO_NEXT);
+ packetList[i] = encodeHeaderAndBody(skfHeader, skfPayload, fragFirstInnerPayload);
getIkeLog()
.d(
"IkeMessage",
@@ -528,7 +526,7 @@
+ "/"
+ totalFragments
+ "): "
- + getIkeLog().pii(packetList[0]));
+ + getIkeLog().pii(packetList[i]));
}
return packetList;
diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeSaPayload.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeSaPayload.java
index d2dd394..40eecf0 100644
--- a/src/java/com/android/internal/net/ipsec/ike/message/IkeSaPayload.java
+++ b/src/java/com/android/internal/net/ipsec/ike/message/IkeSaPayload.java
@@ -514,7 +514,20 @@
Transform[] decodeTransforms(int count, ByteBuffer inputBuffer) throws IkeProtocolException;
}
- // TODO: Add another constructor for building outbound message.
+ /**
+ * Release IPsec SPI resources in the outbound Create Child request
+ *
+ * <p>This method is usually called when an IKE library fails to receive a Create Child response
+ * before it is terminated. It is also safe to call after the Create Child exchange has
+ * succeeded because the newly created IpSecTransform pair will hold the IPsec SPI resource.
+ */
+ public void releaseChildSpiResourcesIfExists() {
+ for (Proposal proposal : proposalList) {
+ if (proposal instanceof ChildProposal) {
+ proposal.releaseSpiResourceIfExists();
+ }
+ }
+ }
/**
* This class represents the common information of an IKE Proposal and a Child Proposal.
diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java
index 66c2517..6fe4aad 100644
--- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java
+++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java
@@ -18,7 +18,9 @@
import static android.net.ipsec.ike.IkeSessionConfiguration.EXTENSION_TYPE_FRAGMENTATION;
import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_EAP_ONLY_AUTH;
+import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED;
import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_CHILD_SA_NOT_FOUND;
+import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INTERNAL_ADDRESS_FAILURE;
import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_SYNTAX;
import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_ADDITIONAL_SAS;
import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN;
@@ -2528,6 +2530,48 @@
}
@Test
+ public void testAuthHandlesIkeErrorNotify() throws Exception {
+ mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuth);
+ verifyRetransmissionStarted();
+ resetMockIkeMessageHelper();
+
+ // Mock rejecting IKE AUTH with Authenticatio Failure notification
+ ReceivedIkePacket mockAuthFailPacket =
+ makeIkeAuthRespWithoutChildPayloads(
+ Arrays.asList(new IkeNotifyPayload(ERROR_TYPE_AUTHENTICATION_FAILED)));
+ mIkeSessionStateMachine.sendMessage(CMD_RECEIVE_IKE_PACKET, mockAuthFailPacket);
+ mLooper.dispatchAll();
+
+ // Verify IKE Session is closed properly
+ assertNull(mIkeSessionStateMachine.getCurrentState());
+ verify(mMockIkeSessionCallback)
+ .onClosedExceptionally(any(AuthenticationFailedException.class));
+ }
+
+ @Test
+ public void testAuthHandlesCreateChildErrorNotify() throws Exception {
+ mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuth);
+ verifyRetransmissionStarted();
+ resetMockIkeMessageHelper();
+
+ // Mock rejecting IKE AUTH with a Create Child error notification
+ ReceivedIkePacket mockAuthFailPacket =
+ makeIkeAuthRespWithoutChildPayloads(
+ Arrays.asList(new IkeNotifyPayload(ERROR_TYPE_INTERNAL_ADDRESS_FAILURE)));
+ mIkeSessionStateMachine.sendMessage(CMD_RECEIVE_IKE_PACKET, mockAuthFailPacket);
+ mLooper.dispatchAll();
+
+ // Verify IKE Session is closed properly
+ assertNull(mIkeSessionStateMachine.getCurrentState());
+
+ ArgumentCaptor<IkeProtocolException> captor =
+ ArgumentCaptor.forClass(IkeProtocolException.class);
+ verify(mMockIkeSessionCallback).onClosedExceptionally(captor.capture());
+ IkeProtocolException exception = captor.getValue();
+ assertEquals(ERROR_TYPE_INTERNAL_ADDRESS_FAILURE, exception.getErrorType());
+ }
+
+ @Test
public void testAuthPskHandleRespWithParsingError() throws Exception {
mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuth);
verifyRetransmissionStarted();