Merge "Verify VPN can handle network loss"
This commit is contained in:
@@ -41,6 +41,7 @@ import static org.junit.Assert.assertArrayEquals;
|
|||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
import static org.junit.Assume.assumeTrue;
|
import static org.junit.Assume.assumeTrue;
|
||||||
@@ -177,6 +178,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@@ -279,6 +281,8 @@ public class VpnTest {
|
|||||||
@Mock private ConnectivityManager mConnectivityManager;
|
@Mock private ConnectivityManager mConnectivityManager;
|
||||||
@Mock private IpSecService mIpSecService;
|
@Mock private IpSecService mIpSecService;
|
||||||
@Mock private VpnProfileStore mVpnProfileStore;
|
@Mock private VpnProfileStore mVpnProfileStore;
|
||||||
|
@Mock private ScheduledThreadPoolExecutor mExecutor;
|
||||||
|
@Mock private ScheduledFuture mScheduledFuture;
|
||||||
@Mock DeviceIdleInternal mDeviceIdleInternal;
|
@Mock DeviceIdleInternal mDeviceIdleInternal;
|
||||||
private final VpnProfile mVpnProfile;
|
private final VpnProfile mVpnProfile;
|
||||||
|
|
||||||
@@ -342,7 +346,9 @@ public class VpnTest {
|
|||||||
// PERMISSION_DENIED.
|
// PERMISSION_DENIED.
|
||||||
doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(any());
|
doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(any());
|
||||||
|
|
||||||
|
// Set up mIkev2SessionCreator and mExecutor
|
||||||
resetIkev2SessionCreator(mIkeSessionWrapper);
|
resetIkev2SessionCreator(mIkeSessionWrapper);
|
||||||
|
resetExecutor(mScheduledFuture);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetIkev2SessionCreator(Vpn.IkeSessionWrapper ikeSession) {
|
private void resetIkev2SessionCreator(Vpn.IkeSessionWrapper ikeSession) {
|
||||||
@@ -351,6 +357,18 @@ public class VpnTest {
|
|||||||
.thenReturn(ikeSession);
|
.thenReturn(ikeSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void resetExecutor(ScheduledFuture scheduledFuture) {
|
||||||
|
doAnswer(
|
||||||
|
(invocation) -> {
|
||||||
|
((Runnable) invocation.getArgument(0)).run();
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.when(mExecutor)
|
||||||
|
.execute(any());
|
||||||
|
when(mExecutor.schedule(
|
||||||
|
any(Runnable.class), anyLong(), any())).thenReturn(mScheduledFuture);
|
||||||
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(CONTROL_VPN);
|
doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(CONTROL_VPN);
|
||||||
@@ -1372,10 +1390,6 @@ public class VpnTest {
|
|||||||
final ArgumentCaptor<IkeSessionCallback> captor =
|
final ArgumentCaptor<IkeSessionCallback> captor =
|
||||||
ArgumentCaptor.forClass(IkeSessionCallback.class);
|
ArgumentCaptor.forClass(IkeSessionCallback.class);
|
||||||
|
|
||||||
// This test depends on a real ScheduledThreadPoolExecutor
|
|
||||||
doReturn(new ScheduledThreadPoolExecutor(1)).when(mTestDeps)
|
|
||||||
.newScheduledThreadPoolExecutor();
|
|
||||||
|
|
||||||
final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
|
final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
|
||||||
when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
|
when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
|
||||||
.thenReturn(mVpnProfile.encode());
|
.thenReturn(mVpnProfile.encode());
|
||||||
@@ -1400,25 +1414,38 @@ public class VpnTest {
|
|||||||
verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS))
|
verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS))
|
||||||
.unregisterNetworkCallback(eq(cb));
|
.unregisterNetworkCallback(eq(cb));
|
||||||
} else if (errorType == VpnManager.ERROR_CLASS_RECOVERABLE) {
|
} else if (errorType == VpnManager.ERROR_CLASS_RECOVERABLE) {
|
||||||
// To prevent spending much time to test the retry function, only retry 2 times here.
|
|
||||||
int retryIndex = 0;
|
int retryIndex = 0;
|
||||||
verify(mIkev2SessionCreator,
|
final IkeSessionCallback ikeCb2 = verifyRetryAndGetNewIkeCb(retryIndex++);
|
||||||
timeout(((TestDeps) vpn.mDeps).getNextRetryDelaySeconds(retryIndex++) * 1000
|
|
||||||
+ TEST_TIMEOUT_MS))
|
|
||||||
.createIkeSession(any(), any(), any(), any(), captor.capture(), any());
|
|
||||||
|
|
||||||
// Capture a new IkeSessionCallback to get the latest token.
|
|
||||||
reset(mIkev2SessionCreator);
|
|
||||||
final IkeSessionCallback ikeCb2 = captor.getValue();
|
|
||||||
ikeCb2.onClosedWithException(exception);
|
ikeCb2.onClosedWithException(exception);
|
||||||
verify(mIkev2SessionCreator,
|
verifyRetryAndGetNewIkeCb(retryIndex++);
|
||||||
timeout(((TestDeps) vpn.mDeps).getNextRetryDelaySeconds(retryIndex++) * 1000
|
|
||||||
+ TEST_TIMEOUT_MS))
|
|
||||||
.createIkeSession(any(), any(), any(), any(), captor.capture(), any());
|
|
||||||
reset(mIkev2SessionCreator);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IkeSessionCallback verifyRetryAndGetNewIkeCb(int retryIndex) {
|
||||||
|
final ArgumentCaptor<Runnable> runnableCaptor =
|
||||||
|
ArgumentCaptor.forClass(Runnable.class);
|
||||||
|
final ArgumentCaptor<IkeSessionCallback> ikeCbCaptor =
|
||||||
|
ArgumentCaptor.forClass(IkeSessionCallback.class);
|
||||||
|
|
||||||
|
// Verify retry is scheduled
|
||||||
|
final long expectedDelay = mTestDeps.getNextRetryDelaySeconds(retryIndex);
|
||||||
|
verify(mExecutor).schedule(runnableCaptor.capture(), eq(expectedDelay), any());
|
||||||
|
|
||||||
|
// Mock the event of firing the retry task
|
||||||
|
runnableCaptor.getValue().run();
|
||||||
|
|
||||||
|
verify(mIkev2SessionCreator)
|
||||||
|
.createIkeSession(any(), any(), any(), any(), ikeCbCaptor.capture(), any());
|
||||||
|
|
||||||
|
// Forget the mIkev2SessionCreator#createIkeSession call and mExecutor#schedule call
|
||||||
|
// for the next retry verification
|
||||||
|
resetIkev2SessionCreator(mIkeSessionWrapper);
|
||||||
|
resetExecutor(mScheduledFuture);
|
||||||
|
|
||||||
|
return ikeCbCaptor.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStartPlatformVpnAuthenticationFailed() throws Exception {
|
public void testStartPlatformVpnAuthenticationFailed() throws Exception {
|
||||||
final IkeProtocolException exception = mock(IkeProtocolException.class);
|
final IkeProtocolException exception = mock(IkeProtocolException.class);
|
||||||
@@ -1685,9 +1712,13 @@ public class VpnTest {
|
|||||||
final PlatformVpnSnapshot vpnSnapShot = verifySetupPlatformVpn(
|
final PlatformVpnSnapshot vpnSnapShot = verifySetupPlatformVpn(
|
||||||
createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */));
|
createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */));
|
||||||
|
|
||||||
// Mock network switch
|
// Mock network loss and verify a cleanup task is scheduled
|
||||||
vpnSnapShot.nwCb.onLost(TEST_NETWORK);
|
vpnSnapShot.nwCb.onLost(TEST_NETWORK);
|
||||||
|
verify(mExecutor).schedule(any(Runnable.class), anyLong(), any());
|
||||||
|
|
||||||
|
// Mock new network comes up and the cleanup task is cancelled
|
||||||
vpnSnapShot.nwCb.onAvailable(TEST_NETWORK_2);
|
vpnSnapShot.nwCb.onAvailable(TEST_NETWORK_2);
|
||||||
|
verify(mScheduledFuture).cancel(anyBoolean());
|
||||||
|
|
||||||
// Verify MOBIKE is triggered
|
// Verify MOBIKE is triggered
|
||||||
verify(mIkeSessionWrapper).setNetwork(TEST_NETWORK_2);
|
verify(mIkeSessionWrapper).setNetwork(TEST_NETWORK_2);
|
||||||
@@ -1755,7 +1786,55 @@ public class VpnTest {
|
|||||||
vpnSnapShot.vpn.mVpnRunner.exitVpnRunner();
|
vpnSnapShot.vpn.mVpnRunner.exitVpnRunner();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add a test for network loss without mobility
|
private void verifyHandlingNetworkLoss() throws Exception {
|
||||||
|
final ArgumentCaptor<LinkProperties> lpCaptor =
|
||||||
|
ArgumentCaptor.forClass(LinkProperties.class);
|
||||||
|
verify(mMockNetworkAgent).sendLinkProperties(lpCaptor.capture());
|
||||||
|
final LinkProperties lp = lpCaptor.getValue();
|
||||||
|
|
||||||
|
assertNull(lp.getInterfaceName());
|
||||||
|
final List<RouteInfo> expectedRoutes = Arrays.asList(
|
||||||
|
new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null /*gateway*/,
|
||||||
|
null /*iface*/, RTN_UNREACHABLE),
|
||||||
|
new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null /*gateway*/,
|
||||||
|
null /*iface*/, RTN_UNREACHABLE));
|
||||||
|
assertEquals(expectedRoutes, lp.getRoutes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStartPlatformVpnHandlesNetworkLoss_mobikeEnabled() throws Exception {
|
||||||
|
final PlatformVpnSnapshot vpnSnapShot = verifySetupPlatformVpn(
|
||||||
|
createIkeConfig(createIkeConnectInfo(), false /* isMobikeEnabled */));
|
||||||
|
|
||||||
|
// Forget the #sendLinkProperties during first setup.
|
||||||
|
reset(mMockNetworkAgent);
|
||||||
|
|
||||||
|
final ArgumentCaptor<Runnable> runnableCaptor =
|
||||||
|
ArgumentCaptor.forClass(Runnable.class);
|
||||||
|
|
||||||
|
// Mock network loss
|
||||||
|
vpnSnapShot.nwCb.onLost(TEST_NETWORK);
|
||||||
|
|
||||||
|
// Mock the grace period expires
|
||||||
|
verify(mExecutor).schedule(runnableCaptor.capture(), anyLong(), any());
|
||||||
|
runnableCaptor.getValue().run();
|
||||||
|
|
||||||
|
verifyHandlingNetworkLoss();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStartPlatformVpnHandlesNetworkLoss_mobikeDisabled() throws Exception {
|
||||||
|
final PlatformVpnSnapshot vpnSnapShot = verifySetupPlatformVpn(
|
||||||
|
createIkeConfig(createIkeConnectInfo(), false /* isMobikeEnabled */));
|
||||||
|
|
||||||
|
// Forget the #sendLinkProperties during first setup.
|
||||||
|
reset(mMockNetworkAgent);
|
||||||
|
|
||||||
|
// Mock network loss
|
||||||
|
vpnSnapShot.nwCb.onLost(TEST_NETWORK);
|
||||||
|
|
||||||
|
verifyHandlingNetworkLoss();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStartRacoonNumericAddress() throws Exception {
|
public void testStartRacoonNumericAddress() throws Exception {
|
||||||
@@ -1981,16 +2060,7 @@ public class VpnTest {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ScheduledThreadPoolExecutor newScheduledThreadPoolExecutor() {
|
public ScheduledThreadPoolExecutor newScheduledThreadPoolExecutor() {
|
||||||
final ScheduledThreadPoolExecutor mockExecutor =
|
return mExecutor;
|
||||||
mock(ScheduledThreadPoolExecutor.class);
|
|
||||||
doAnswer(
|
|
||||||
(invocation) -> {
|
|
||||||
((Runnable) invocation.getArgument(0)).run();
|
|
||||||
return null;
|
|
||||||
})
|
|
||||||
.when(mockExecutor)
|
|
||||||
.execute(any());
|
|
||||||
return mockExecutor;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user