Add testLockdownVpn that mocks platform VPN.

Add a test for lockdown vpn that uses TYPE_IKEV2_IPSEC_PSK and
mocks platform VPN by override in startLegacyVpnPrivileged().
In the context of ConnectivityService, setVpnDefaultForUids()
is the main interaction.

Refactor testLegacyLockdownVpn to take a VpnProfile and assert
behaviors with and without setVpnDefaultForUids().
This includes:
    1. Updating callback asserts and assertActiveNetworkInfo to
       reflect setVpnDefaultForUids().
    2. Adding TODOs where mCm.getActiveNetworkInfo() returns
       unexpected values.

Bug: 230548427
Test: atest FrameworksNetTests
Change-Id: Ida4a4bc745af5ba2fc251795b2ffca56ead79b7f
This commit is contained in:
Hansen Kurli
2023-08-09 15:17:24 +08:00
parent 78b06ebe84
commit 48c14686a3

View File

@@ -76,6 +76,7 @@ import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPR
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OFF;
@@ -465,6 +466,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
@@ -1494,6 +1496,7 @@ public class ConnectivityServiceTest {
private int mVpnType = VpnManager.TYPE_VPN_SERVICE;
private UnderlyingNetworkInfo mUnderlyingNetworkInfo;
private String mSessionKey;
public MockVpn(int userId) {
super(startHandlerThreadAndReturnLooper(), mServiceContext,
@@ -1658,10 +1661,28 @@ public class ConnectivityServiceTest {
updateState(DetailedState.CONNECTING, "startLegacyVpn");
}
// Mock the interaction of IkeV2VpnRunner start. In the context of ConnectivityService,
// setVpnDefaultForUids() is the main interaction and a sessionKey is stored.
private synchronized void startPlatformVpn() {
updateState(DetailedState.CONNECTING, "startPlatformVpn");
mSessionKey = UUID.randomUUID().toString();
// Assuming no disallowed applications
final Set<Range<Integer>> ranges = UidRange.toIntRanges(Set.of(PRIMARY_UIDRANGE));
mCm.setVpnDefaultForUids(mSessionKey, ranges);
// Wait for vpn network preference updates.
waitForIdle();
}
@Override
public void startLegacyVpnPrivileged(VpnProfile profile,
@Nullable Network underlying, @NonNull LinkProperties egress) {
switch (profile.type) {
case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
case VpnProfile.TYPE_IKEV2_FROM_IKE_TUN_CONN_PARAMS:
startPlatformVpn();
break;
case VpnProfile.TYPE_L2TP_IPSEC_PSK:
case VpnProfile.TYPE_L2TP_IPSEC_RSA:
case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
@@ -1676,6 +1697,11 @@ public class ConnectivityServiceTest {
@Override
public synchronized void stopVpnRunnerPrivileged() {
if (mSessionKey != null) {
// Clear vpn network preference.
mCm.setVpnDefaultForUids(mSessionKey, Collections.EMPTY_LIST);
mSessionKey = null;
}
disconnect();
}
@@ -10132,7 +10158,7 @@ public class ConnectivityServiceTest {
doAsUid(Process.SYSTEM_UID, () -> mCm.unregisterNetworkCallback(perUidCb));
}
private VpnProfile setupLegacyLockdownVpn() {
private VpnProfile setupLockdownVpn(int profileType) {
final String profileName = "testVpnProfile";
final byte[] profileTag = profileName.getBytes(StandardCharsets.UTF_8);
doReturn(profileTag).when(mVpnProfileStore).get(Credentials.LOCKDOWN_VPN);
@@ -10141,7 +10167,9 @@ public class ConnectivityServiceTest {
profile.name = "My VPN";
profile.server = "192.0.2.1";
profile.dnsServers = "8.8.8.8";
profile.type = VpnProfile.TYPE_IPSEC_XAUTH_PSK;
profile.ipsecIdentifier = "My ipsecIdentifier";
profile.ipsecSecret = "My PSK";
profile.type = profileType;
final byte[] encodedProfile = profile.encode();
doReturn(encodedProfile).when(mVpnProfileStore).get(Credentials.VPN + profileName);
@@ -10159,8 +10187,8 @@ public class ConnectivityServiceTest {
mMockVpn.connect(true);
}
@Test
public void testLegacyLockdownVpn() throws Exception {
private void doTestLockdownVpn(VpnProfile profile, boolean expectSetVpnDefaultForUids)
throws Exception {
mServiceContext.setPermission(
Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED);
@@ -10175,9 +10203,6 @@ public class ConnectivityServiceTest {
mCm.registerSystemDefaultNetworkCallback(systemDefaultCallback,
new Handler(ConnectivityThread.getInstanceLooper()));
// Pretend lockdown VPN was configured.
final VpnProfile profile = setupLegacyLockdownVpn();
// Init lockdown state to simulate LockdownVpnTracker behavior.
mCm.setLegacyLockdownVpnEnabled(true);
mMockVpn.setEnableTeardown(false);
@@ -10200,7 +10225,17 @@ public class ConnectivityServiceTest {
// Simulate LockdownVpnTracker attempting to start the VPN since it received the
// systemDefault callback.
mMockVpn.startLegacyVpnPrivileged(profile, mCellAgent.getNetwork(), cellLp);
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
if (expectSetVpnDefaultForUids) {
// setVpnDefaultForUids() releases the original network request and creates a VPN
// request so LOST callback is received.
defaultCallback.expect(LOST, mCellAgent);
// Due to the VPN default request, getActiveNetworkInfo() gets the mNoServiceNetwork
// as the network satisfier which has TYPE_NONE.
// TODO: This should not be TYPE_NONE, see ConnectivityManager#getActiveNetworkInfo
assertActiveNetworkInfo(TYPE_NONE, DetailedState.BLOCKED);
} else {
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
}
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED);
@@ -10215,7 +10250,16 @@ public class ConnectivityServiceTest {
final NetworkCapabilities vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
b2.expectBroadcast();
b3.expectBroadcast();
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
if (expectSetVpnDefaultForUids) {
// Due to the VPN default request, getActiveNetworkInfo() gets the VPN network as the
// network satisfier which has TYPE_VPN.
assertActiveNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
} else {
// LegacyVpnRunner does not call setVpnDefaultsForUids(), which means
// getActiveNetworkInfo() can only return the info for the system-wide default instead.
// This should be fixed, but LegacyVpnRunner will be removed soon anyway.
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
}
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
@@ -10256,12 +10300,26 @@ public class ConnectivityServiceTest {
// VPN network is disconnected (to restart)
callback.expect(LOST, mMockVpn);
defaultCallback.expect(LOST, mMockVpn);
// The network preference is cleared when VPN is disconnected so it receives callbacks for
// the system-wide default.
defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiAgent);
if (expectSetVpnDefaultForUids) {
// setVpnDefaultForUids() releases the original network request and creates a VPN
// request so LOST callback is received.
defaultCallback.expect(LOST, mWiFiAgent);
}
systemDefaultCallback.assertNoCallback();
b6.expectBroadcast();
// While the VPN is reconnecting on the new network, everything is blocked.
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
if (expectSetVpnDefaultForUids) {
// Due to the VPN default request, getActiveNetworkInfo() gets the mNoServiceNetwork
// as the network satisfier which has TYPE_NONE.
// TODO: This should not be TYPE_NONE, see ConnectivityManager#getActiveNetworkInfo
assertActiveNetworkInfo(TYPE_NONE, DetailedState.BLOCKED);
} else {
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
}
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED);
@@ -10276,7 +10334,16 @@ public class ConnectivityServiceTest {
systemDefaultCallback.assertNoCallback();
b7.expectBroadcast();
b8.expectBroadcast();
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
if (expectSetVpnDefaultForUids) {
// Due to the VPN default request, getActiveNetworkInfo() gets the VPN network as the
// network satisfier which has TYPE_VPN.
assertActiveNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
} else {
// LegacyVpnRunner does not call setVpnDefaultsForUids(), which means
// getActiveNetworkInfo() can only return the info for the system-wide default instead.
// This should be fixed, but LegacyVpnRunner will be removed soon anyway.
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
}
assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
@@ -10293,7 +10360,16 @@ public class ConnectivityServiceTest {
defaultCallback.assertNoCallback();
systemDefaultCallback.assertNoCallback();
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
if (expectSetVpnDefaultForUids) {
// Due to the VPN default request, getActiveNetworkInfo() gets the VPN network as the
// network satisfier which has TYPE_VPN.
assertActiveNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
} else {
// LegacyVpnRunner does not call setVpnDefaultsForUids(), which means
// getActiveNetworkInfo() can only return the info for the system-wide default instead.
// This should be fixed, but LegacyVpnRunner will be removed soon anyway.
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
}
assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
@@ -10323,6 +10399,18 @@ public class ConnectivityServiceTest {
assertNoCallbacks(callback, defaultCallback, systemDefaultCallback);
}
@Test
public void testLockdownVpn_LegacyVpnRunner() throws Exception {
final VpnProfile profile = setupLockdownVpn(VpnProfile.TYPE_IPSEC_XAUTH_PSK);
doTestLockdownVpn(profile, false /* expectSetVpnDefaultForUids */);
}
@Test
public void testLockdownVpn_Ikev2VpnRunner() throws Exception {
final VpnProfile profile = setupLockdownVpn(VpnProfile.TYPE_IKEV2_IPSEC_PSK);
doTestLockdownVpn(profile, true /* expectSetVpnDefaultForUids */);
}
@Test @IgnoreUpTo(Build.VERSION_CODES.S_V2)
public void testLockdownSetFirewallUidRule() throws Exception {
final Set<Range<Integer>> lockdownRange = UidRange.toIntRanges(Set.of(PRIMARY_UIDRANGE));