• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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