1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.vcn; 18 19 import static android.net.IpSecManager.DIRECTION_FWD; 20 import static android.net.IpSecManager.DIRECTION_IN; 21 import static android.net.IpSecManager.DIRECTION_OUT; 22 import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; 23 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; 24 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; 25 import static android.net.NetworkCapabilities.TRANSPORT_WIFI; 26 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED; 27 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_TEMPORARY_FAILURE; 28 import static android.net.vcn.VcnManager.VCN_ERROR_CODE_CONFIG_ERROR; 29 import static android.net.vcn.VcnManager.VCN_ERROR_CODE_INTERNAL_ERROR; 30 import static android.net.vcn.VcnManager.VCN_ERROR_CODE_NETWORK_ERROR; 31 32 import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration; 33 import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession; 34 import static com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent; 35 36 import static org.junit.Assert.assertEquals; 37 import static org.junit.Assert.assertFalse; 38 import static org.junit.Assert.assertTrue; 39 import static org.mockito.Matchers.any; 40 import static org.mockito.Matchers.anyInt; 41 import static org.mockito.Matchers.argThat; 42 import static org.mockito.Matchers.eq; 43 import static org.mockito.Mockito.doReturn; 44 import static org.mockito.Mockito.mock; 45 import static org.mockito.Mockito.never; 46 import static org.mockito.Mockito.times; 47 import static org.mockito.Mockito.verify; 48 import static org.mockito.Mockito.verifyNoMoreInteractions; 49 import static org.mockito.Mockito.when; 50 51 import static java.util.Collections.singletonList; 52 53 import android.net.ConnectivityManager; 54 import android.net.LinkAddress; 55 import android.net.LinkProperties; 56 import android.net.NetworkAgent; 57 import android.net.NetworkCapabilities; 58 import android.net.ipsec.ike.ChildSaProposal; 59 import android.net.ipsec.ike.exceptions.IkeException; 60 import android.net.ipsec.ike.exceptions.IkeInternalException; 61 import android.net.ipsec.ike.exceptions.IkeProtocolException; 62 import android.net.vcn.VcnGatewayConnectionConfig; 63 import android.net.vcn.VcnGatewayConnectionConfigTest; 64 import android.net.vcn.VcnManager.VcnErrorCode; 65 66 import androidx.test.filters.SmallTest; 67 import androidx.test.runner.AndroidJUnit4; 68 69 import com.android.server.vcn.util.MtuUtils; 70 71 import org.junit.Before; 72 import org.junit.Test; 73 import org.junit.runner.RunWith; 74 import org.mockito.ArgumentCaptor; 75 76 import java.io.IOException; 77 import java.net.InetAddress; 78 import java.net.UnknownHostException; 79 import java.util.Arrays; 80 import java.util.Collections; 81 import java.util.List; 82 import java.util.function.Consumer; 83 84 /** Tests for VcnGatewayConnection.ConnectedState */ 85 @RunWith(AndroidJUnit4.class) 86 @SmallTest 87 public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnectionTestBase { 88 private VcnIkeSession mIkeSession; 89 private VcnNetworkAgent mNetworkAgent; 90 91 @Before setUp()92 public void setUp() throws Exception { 93 super.setUp(); 94 95 mNetworkAgent = mock(VcnNetworkAgent.class); 96 doReturn(mNetworkAgent) 97 .when(mDeps) 98 .newNetworkAgent(any(), any(), any(), any(), any(), any(), any(), any(), any()); 99 100 mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1); 101 102 mIkeSession = mGatewayConnection.buildIkeSession(TEST_UNDERLYING_NETWORK_RECORD_1.network); 103 mGatewayConnection.setIkeSession(mIkeSession); 104 105 mGatewayConnection.transitionTo(mGatewayConnection.mConnectedState); 106 mTestLooper.dispatchAll(); 107 } 108 109 @Test testEnterStateCreatesNewIkeSession()110 public void testEnterStateCreatesNewIkeSession() throws Exception { 111 verify(mDeps).newIkeSession(any(), any(), any(), any(), any()); 112 } 113 114 @Test testEnterStateDoesNotCancelSafeModeAlarm()115 public void testEnterStateDoesNotCancelSafeModeAlarm() { 116 verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */); 117 } 118 119 @Test testNullNetworkDoesNotTriggerDisconnect()120 public void testNullNetworkDoesNotTriggerDisconnect() throws Exception { 121 mGatewayConnection 122 .getUnderlyingNetworkTrackerCallback() 123 .onSelectedUnderlyingNetworkChanged(null); 124 mTestLooper.dispatchAll(); 125 126 assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState()); 127 verify(mIkeSession, never()).close(); 128 verifyDisconnectRequestAlarmAndGetCallback(false /* expectCanceled */); 129 } 130 131 @Test testNewNetworkTriggersMigration()132 public void testNewNetworkTriggersMigration() throws Exception { 133 mGatewayConnection 134 .getUnderlyingNetworkTrackerCallback() 135 .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2); 136 mTestLooper.dispatchAll(); 137 138 assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState()); 139 verify(mIkeSession, never()).close(); 140 verify(mIkeSession).setNetwork(TEST_UNDERLYING_NETWORK_RECORD_2.network); 141 } 142 143 @Test testSameNetworkDoesNotTriggerMigration()144 public void testSameNetworkDoesNotTriggerMigration() throws Exception { 145 mGatewayConnection 146 .getUnderlyingNetworkTrackerCallback() 147 .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1); 148 mTestLooper.dispatchAll(); 149 150 assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState()); 151 } 152 verifyVcnTransformsApplied( VcnGatewayConnection vcnGatewayConnection, boolean expectForwardTransform)153 private void verifyVcnTransformsApplied( 154 VcnGatewayConnection vcnGatewayConnection, boolean expectForwardTransform) 155 throws Exception { 156 for (int direction : new int[] {DIRECTION_IN, DIRECTION_OUT}) { 157 getChildSessionCallback().onIpSecTransformCreated(makeDummyIpSecTransform(), direction); 158 mTestLooper.dispatchAll(); 159 160 verify(mIpSecSvc) 161 .applyTunnelModeTransform( 162 eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(direction), anyInt(), any()); 163 } 164 165 verify(mIpSecSvc, expectForwardTransform ? times(1) : never()) 166 .applyTunnelModeTransform( 167 eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(DIRECTION_FWD), anyInt(), any()); 168 169 assertEquals(vcnGatewayConnection.mConnectedState, vcnGatewayConnection.getCurrentState()); 170 } 171 172 @Test testCreatedTransformsAreApplied()173 public void testCreatedTransformsAreApplied() throws Exception { 174 verifyVcnTransformsApplied(mGatewayConnection, false /* expectForwardTransform */); 175 } 176 177 @Test testCreatedTransformsAreAppliedWithDun()178 public void testCreatedTransformsAreAppliedWithDun() throws Exception { 179 VcnGatewayConnectionConfig gatewayConfig = 180 VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps( 181 NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN); 182 VcnGatewayConnection gatewayConnection = 183 new VcnGatewayConnection( 184 mVcnContext, 185 TEST_SUB_GRP, 186 TEST_SUBSCRIPTION_SNAPSHOT, 187 gatewayConfig, 188 mGatewayStatusCallback, 189 true /* isMobileDataEnabled */, 190 mDeps); 191 gatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1); 192 final VcnIkeSession session = 193 gatewayConnection.buildIkeSession(TEST_UNDERLYING_NETWORK_RECORD_1.network); 194 gatewayConnection.setIkeSession(session); 195 gatewayConnection.transitionTo(gatewayConnection.mConnectedState); 196 mTestLooper.dispatchAll(); 197 198 verifyVcnTransformsApplied(gatewayConnection, true /* expectForwardTransform */); 199 } 200 201 @Test testMigration()202 public void testMigration() throws Exception { 203 triggerChildOpened(); 204 205 mGatewayConnection 206 .getUnderlyingNetworkTrackerCallback() 207 .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2); 208 getChildSessionCallback() 209 .onIpSecTransformsMigrated(makeDummyIpSecTransform(), makeDummyIpSecTransform()); 210 mTestLooper.dispatchAll(); 211 212 verify(mIpSecSvc, times(2)) 213 .setNetworkForTunnelInterface( 214 eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), 215 eq(TEST_UNDERLYING_NETWORK_RECORD_2.network), 216 any()); 217 218 for (int direction : new int[] {DIRECTION_IN, DIRECTION_OUT}) { 219 verify(mIpSecSvc) 220 .applyTunnelModeTransform( 221 eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(direction), anyInt(), any()); 222 } 223 224 assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState()); 225 226 final List<ChildSaProposal> saProposals = 227 mConfig.getTunnelConnectionParams() 228 .getTunnelModeChildSessionParams() 229 .getSaProposals(); 230 final int expectedMtu = 231 MtuUtils.getMtu( 232 saProposals, 233 mConfig.getMaxMtu(), 234 TEST_UNDERLYING_NETWORK_RECORD_2.linkProperties.getMtu()); 235 verify(mNetworkAgent).sendLinkProperties( 236 argThat(lp -> expectedMtu == lp.getMtu() 237 && TEST_TCP_BUFFER_SIZES_2.equals(lp.getTcpBufferSizes()))); 238 verify(mNetworkAgent) 239 .setUnderlyingNetworks(eq(singletonList(TEST_UNDERLYING_NETWORK_RECORD_2.network))); 240 } 241 triggerChildOpened()242 private void triggerChildOpened() { 243 triggerChildOpened(Collections.singletonList(TEST_INTERNAL_ADDR), TEST_DNS_ADDR); 244 } 245 triggerChildOpened(List<LinkAddress> internalAddresses, InetAddress dnsAddress)246 private void triggerChildOpened(List<LinkAddress> internalAddresses, InetAddress dnsAddress) { 247 final VcnChildSessionConfiguration mMockChildSessionConfig = 248 mock(VcnChildSessionConfiguration.class); 249 doReturn(internalAddresses).when(mMockChildSessionConfig).getInternalAddresses(); 250 doReturn(Collections.singletonList(dnsAddress)) 251 .when(mMockChildSessionConfig) 252 .getInternalDnsServers(); 253 254 getChildSessionCallback().onOpened(mMockChildSessionConfig); 255 } 256 triggerValidation(int status)257 private void triggerValidation(int status) { 258 final ArgumentCaptor<Consumer<Integer>> validationCallbackCaptor = 259 ArgumentCaptor.forClass(Consumer.class); 260 verify(mDeps) 261 .newNetworkAgent( 262 any(), 263 any(), 264 any(), 265 any(), 266 any(), 267 any(), 268 any(), 269 any(), 270 validationCallbackCaptor.capture()); 271 272 validationCallbackCaptor.getValue().accept(status); 273 } 274 275 @Test testChildOpenedRegistersNetwork()276 public void testChildOpenedRegistersNetwork() throws Exception { 277 // Verify scheduled but not canceled when entering ConnectedState 278 verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */); 279 triggerChildOpened(); 280 mTestLooper.dispatchAll(); 281 282 assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState()); 283 284 final ArgumentCaptor<LinkProperties> lpCaptor = 285 ArgumentCaptor.forClass(LinkProperties.class); 286 final ArgumentCaptor<NetworkCapabilities> ncCaptor = 287 ArgumentCaptor.forClass(NetworkCapabilities.class); 288 verify(mDeps) 289 .newNetworkAgent( 290 eq(mVcnContext), 291 any(String.class), 292 ncCaptor.capture(), 293 lpCaptor.capture(), 294 any(), 295 argThat(nac -> nac.getLegacyType() == ConnectivityManager.TYPE_MOBILE), 296 any(), 297 any(), 298 any()); 299 verify(mNetworkAgent).register(); 300 verify(mNetworkAgent) 301 .setUnderlyingNetworks(eq(singletonList(TEST_UNDERLYING_NETWORK_RECORD_1.network))); 302 verify(mNetworkAgent).markConnected(); 303 304 verify(mIpSecSvc) 305 .addAddressToTunnelInterface( 306 eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(TEST_INTERNAL_ADDR), any()); 307 308 final LinkProperties lp = lpCaptor.getValue(); 309 assertEquals(Collections.singletonList(TEST_INTERNAL_ADDR), lp.getLinkAddresses()); 310 assertEquals(Collections.singletonList(TEST_DNS_ADDR), lp.getDnsServers()); 311 assertEquals(TEST_TCP_BUFFER_SIZES_1, lp.getTcpBufferSizes()); 312 313 final NetworkCapabilities nc = ncCaptor.getValue(); 314 assertTrue(nc.hasTransport(TRANSPORT_CELLULAR)); 315 assertFalse(nc.hasTransport(TRANSPORT_WIFI)); 316 for (int cap : mConfig.getAllExposedCapabilities()) { 317 assertTrue(nc.hasCapability(cap)); 318 } 319 320 // Now that Vcn Network is up, notify it as validated and verify the SafeMode alarm is 321 // canceled 322 triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID); 323 verify(mSafeModeTimeoutAlarm).cancel(); 324 assertFalse(mGatewayConnection.isInSafeMode()); 325 } 326 327 @Test testInternalAndDnsAddressesChanged()328 public void testInternalAndDnsAddressesChanged() throws Exception { 329 final List<LinkAddress> startingInternalAddrs = 330 Arrays.asList(new LinkAddress[] {TEST_INTERNAL_ADDR, TEST_INTERNAL_ADDR_2}); 331 triggerChildOpened(startingInternalAddrs, TEST_DNS_ADDR); 332 mTestLooper.dispatchAll(); 333 334 for (LinkAddress addr : startingInternalAddrs) { 335 verify(mIpSecSvc) 336 .addAddressToTunnelInterface( 337 eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(addr), any()); 338 } 339 340 verify(mDeps) 341 .newNetworkAgent( 342 any(), 343 any(), 344 any(), 345 argThat( 346 lp -> 347 startingInternalAddrs.equals(lp.getLinkAddresses()) 348 && Collections.singletonList(TEST_DNS_ADDR) 349 .equals(lp.getDnsServers())), 350 any(), 351 any(), 352 any(), 353 any(), 354 any()); 355 356 // Trigger another connection event, and verify that the addresses change 357 final List<LinkAddress> newInternalAddrs = 358 Arrays.asList(new LinkAddress[] {TEST_INTERNAL_ADDR_2, TEST_INTERNAL_ADDR_3}); 359 triggerChildOpened(newInternalAddrs, TEST_DNS_ADDR_2); 360 mTestLooper.dispatchAll(); 361 362 // Verify addresses on tunnel network added/removed 363 for (LinkAddress addr : newInternalAddrs) { 364 verify(mIpSecSvc) 365 .addAddressToTunnelInterface( 366 eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(addr), any()); 367 } 368 verify(mIpSecSvc) 369 .removeAddressFromTunnelInterface( 370 eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(TEST_INTERNAL_ADDR), any()); 371 372 verify(mNetworkAgent).sendLinkProperties(argThat( 373 lp -> newInternalAddrs.equals(lp.getLinkAddresses()) 374 && Collections.singletonList(TEST_DNS_ADDR_2).equals(lp.getDnsServers()))); 375 376 // Verify that IpSecTunnelInterface only created once 377 verify(mIpSecSvc).createTunnelInterface(any(), any(), any(), any(), any()); 378 verifyNoMoreInteractions(mIpSecSvc); 379 } 380 381 @Test testSuccessfulConnectionExitsSafeMode()382 public void testSuccessfulConnectionExitsSafeMode() throws Exception { 383 verifySafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent( 384 mGatewayConnection.mConnectedState); 385 386 assertTrue(mGatewayConnection.isInSafeMode()); 387 assertFalse(mGatewayConnection.isQuitting()); 388 389 triggerChildOpened(); 390 mTestLooper.dispatchAll(); 391 392 triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID); 393 394 assertFalse(mGatewayConnection.isInSafeMode()); 395 } 396 397 @Test testSubsequentFailedValidationTriggersSafeMode()398 public void testSubsequentFailedValidationTriggersSafeMode() throws Exception { 399 triggerChildOpened(); 400 mTestLooper.dispatchAll(); 401 402 triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID); 403 assertFalse(mGatewayConnection.isInSafeMode()); 404 405 // Trigger a failed validation, and the subsequent safemode timeout. 406 triggerValidation(NetworkAgent.VALIDATION_STATUS_NOT_VALID); 407 mTestLooper.dispatchAll(); 408 409 final ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class); 410 verify(mDeps, times(2)) 411 .newWakeupMessage( 412 eq(mVcnContext), 413 any(), 414 eq(VcnGatewayConnection.SAFEMODE_TIMEOUT_ALARM), 415 runnableCaptor.capture()); 416 runnableCaptor.getValue().run(); 417 mTestLooper.dispatchAll(); 418 419 assertTrue(mGatewayConnection.isInSafeMode()); 420 } 421 setupNetworkAndGetUnwantedCallback()422 private Consumer<VcnNetworkAgent> setupNetworkAndGetUnwantedCallback() { 423 triggerChildOpened(); 424 mTestLooper.dispatchAll(); 425 426 final ArgumentCaptor<Consumer<VcnNetworkAgent>> unwantedCallbackCaptor = 427 ArgumentCaptor.forClass(Consumer.class); 428 verify(mDeps) 429 .newNetworkAgent( 430 any(), 431 any(), 432 any(), 433 any(), 434 any(), 435 any(), 436 any(), 437 unwantedCallbackCaptor.capture(), 438 any()); 439 440 return unwantedCallbackCaptor.getValue(); 441 } 442 443 @Test testUnwantedNetworkAgentTriggersTeardown()444 public void testUnwantedNetworkAgentTriggersTeardown() throws Exception { 445 final Consumer<VcnNetworkAgent> unwantedCallback = setupNetworkAndGetUnwantedCallback(); 446 447 unwantedCallback.accept(mNetworkAgent); 448 mTestLooper.dispatchAll(); 449 450 assertTrue(mGatewayConnection.isQuitting()); 451 assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); 452 } 453 454 @Test testUnwantedNetworkAgentWithDisconnectedNetworkAgent()455 public void testUnwantedNetworkAgentWithDisconnectedNetworkAgent() throws Exception { 456 final Consumer<VcnNetworkAgent> unwantedCallback = setupNetworkAndGetUnwantedCallback(); 457 458 mGatewayConnection.setNetworkAgent(null); 459 unwantedCallback.accept(mNetworkAgent); 460 mTestLooper.dispatchAll(); 461 462 // Verify that the call was ignored; the state machine is still running, and the state has 463 // not changed. 464 assertFalse(mGatewayConnection.isQuitting()); 465 assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState()); 466 } 467 468 @Test testUnwantedNetworkAgentWithNewNetworkAgent()469 public void testUnwantedNetworkAgentWithNewNetworkAgent() throws Exception { 470 final Consumer<VcnNetworkAgent> unwantedCallback = setupNetworkAndGetUnwantedCallback(); 471 final VcnNetworkAgent testAgent = mock(VcnNetworkAgent.class); 472 473 mGatewayConnection.setNetworkAgent(testAgent); 474 unwantedCallback.accept(mNetworkAgent); 475 mTestLooper.dispatchAll(); 476 477 assertFalse(mGatewayConnection.isQuitting()); 478 assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState()); 479 assertEquals(testAgent, mGatewayConnection.getNetworkAgent()); 480 } 481 482 @Test testChildSessionClosedTriggersDisconnect()483 public void testChildSessionClosedTriggersDisconnect() throws Exception { 484 // Verify scheduled but not canceled when entering ConnectedState 485 verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */); 486 487 getChildSessionCallback().onClosed(); 488 mTestLooper.dispatchAll(); 489 490 assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); 491 verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */); 492 493 // Since network never validated, verify mSafeModeTimeoutAlarm not canceled 494 verifyNoMoreInteractions(mSafeModeTimeoutAlarm); 495 496 // The child session was closed without exception, so verify that the GatewayStatusCallback 497 // was not notified 498 verifyNoMoreInteractions(mGatewayStatusCallback); 499 } 500 501 @Test testChildSessionClosedExceptionallyNotifiesGatewayStatusCallback()502 public void testChildSessionClosedExceptionallyNotifiesGatewayStatusCallback() 503 throws Exception { 504 final IkeInternalException exception = new IkeInternalException(mock(IOException.class)); 505 getChildSessionCallback().onClosedExceptionally(exception); 506 mTestLooper.dispatchAll(); 507 508 verify(mGatewayStatusCallback) 509 .onGatewayConnectionError( 510 eq(mConfig.getGatewayConnectionName()), 511 eq(VCN_ERROR_CODE_INTERNAL_ERROR), 512 any(), 513 any()); 514 } 515 516 @Test testIkeSessionClosedTriggersDisconnect()517 public void testIkeSessionClosedTriggersDisconnect() throws Exception { 518 // Verify scheduled but not canceled when entering ConnectedState 519 verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */); 520 521 getIkeSessionCallback().onClosed(); 522 mTestLooper.dispatchAll(); 523 524 assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState()); 525 verify(mIkeSession).close(); 526 527 // Since network never validated, verify mSafeModeTimeoutAlarm not canceled 528 verifyNoMoreInteractions(mSafeModeTimeoutAlarm); 529 530 // IkeSession closed with no error, so verify that the GatewayStatusCallback was not 531 // notified 532 verifyNoMoreInteractions(mGatewayStatusCallback); 533 } 534 verifyIkeSessionClosedExceptionalltyNotifiesStatusCallback( IkeException cause, @VcnErrorCode int expectedErrorType)535 private void verifyIkeSessionClosedExceptionalltyNotifiesStatusCallback( 536 IkeException cause, @VcnErrorCode int expectedErrorType) { 537 getIkeSessionCallback().onClosedExceptionally(cause); 538 mTestLooper.dispatchAll(); 539 540 verify(mIkeSession).close(); 541 542 verify(mGatewayStatusCallback) 543 .onGatewayConnectionError( 544 eq(mConfig.getGatewayConnectionName()), 545 eq(expectedErrorType), 546 any(), 547 any()); 548 } 549 buildMockIkeProtocolException(int errorCode)550 private static IkeProtocolException buildMockIkeProtocolException(int errorCode) { 551 final IkeProtocolException exception = mock(IkeProtocolException.class); 552 when(exception.getErrorType()).thenReturn(errorCode); 553 return exception; 554 } 555 556 @Test testIkeSessionClosedExceptionallyAuthenticationFailure()557 public void testIkeSessionClosedExceptionallyAuthenticationFailure() throws Exception { 558 verifyIkeSessionClosedExceptionalltyNotifiesStatusCallback( 559 buildMockIkeProtocolException(ERROR_TYPE_AUTHENTICATION_FAILED), 560 VCN_ERROR_CODE_CONFIG_ERROR); 561 } 562 563 @Test testIkeSessionClosedExceptionallyDnsFailure()564 public void testIkeSessionClosedExceptionallyDnsFailure() throws Exception { 565 verifyIkeSessionClosedExceptionalltyNotifiesStatusCallback( 566 new IkeInternalException(new UnknownHostException()), VCN_ERROR_CODE_NETWORK_ERROR); 567 } 568 569 @Test testIkeSessionClosedExceptionallyInternalFailure()570 public void testIkeSessionClosedExceptionallyInternalFailure() throws Exception { 571 verifyIkeSessionClosedExceptionalltyNotifiesStatusCallback( 572 buildMockIkeProtocolException(ERROR_TYPE_TEMPORARY_FAILURE), 573 VCN_ERROR_CODE_INTERNAL_ERROR); 574 } 575 576 @Test testTeardown()577 public void testTeardown() throws Exception { 578 mGatewayConnection.teardownAsynchronously(); 579 mTestLooper.dispatchAll(); 580 581 // Verify that sending a non-quitting disconnect request does not unset the isQuitting flag 582 mGatewayConnection.sendDisconnectRequestedAndAcquireWakelock("TEST", false); 583 mTestLooper.dispatchAll(); 584 585 assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); 586 assertTrue(mGatewayConnection.isQuitting()); 587 } 588 589 @Test testNonTeardownDisconnectRequest()590 public void testNonTeardownDisconnectRequest() throws Exception { 591 mGatewayConnection.sendDisconnectRequestedAndAcquireWakelock("TEST", false); 592 mTestLooper.dispatchAll(); 593 594 assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); 595 assertFalse(mGatewayConnection.isQuitting()); 596 } 597 } 598