Merge changes Ic5750d4f,I9fa888c9,I4211475f

* changes:
  Deflake testNetworkBlockedStatusAlwaysOnVpn
  Simplify testVpnRestrictedUsers.
  Add test coverage for LockdownVpnTracker.
This commit is contained in:
Lorenzo Colitti
2021-01-18 14:03:54 +00:00
committed by Gerrit Code Review
2 changed files with 332 additions and 58 deletions

View File

@@ -21,6 +21,7 @@ import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.content.Intent.ACTION_USER_ADDED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.ACTION_USER_UNLOCKED;
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
@@ -221,6 +222,7 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.security.Credentials;
import android.security.KeyStore;
import android.system.Os;
import android.telephony.TelephonyManager;
@@ -237,6 +239,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.app.IBatteryStats;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.WakeupMessage;
import com.android.internal.util.test.BroadcastInterceptingContext;
@@ -279,6 +282,7 @@ import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -384,6 +388,7 @@ public class ConnectivityServiceTest {
@Mock MockableSystemProperties mSystemProperties;
@Mock EthernetManager mEthernetManager;
@Mock NetworkPolicyManager mNetworkPolicyManager;
@Mock KeyStore mKeyStore;
private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -1062,6 +1067,15 @@ public class ConnectivityServiceTest {
private int mVpnType = VpnManager.TYPE_VPN_SERVICE;
private VpnInfo mVpnInfo;
// These ConditionVariables allow tests to wait for LegacyVpnRunner to be stopped/started.
// TODO: this scheme is ad-hoc and error-prone because it does not fail if, for example, the
// test expects two starts in a row, or even if the production code calls start twice in a
// row. find a better solution. Simply putting a method to create a LegacyVpnRunner into
// Vpn.Dependencies doesn't work because LegacyVpnRunner is not a static class and has
// extensive access into the internals of Vpn.
private ConditionVariable mStartLegacyVpnCv = new ConditionVariable();
private ConditionVariable mStopVpnRunnerCv = new ConditionVariable();
public MockVpn(int userId) {
super(startHandlerThreadAndReturnLooper(), mServiceContext,
new Dependencies() {
@@ -1075,7 +1089,7 @@ public class ConnectivityServiceTest {
return mDeviceIdleInternal;
}
},
mNetworkManagementService, mMockNetd, userId, mock(KeyStore.class));
mNetworkManagementService, mMockNetd, userId, mKeyStore);
}
public void setUids(Set<UidRange> uids) {
@@ -1187,9 +1201,43 @@ public class ConnectivityServiceTest {
}
mAgentRegistered = false;
setUids(null);
// Remove NET_CAPABILITY_INTERNET or MockNetworkAgent will refuse to connect later on.
mNetworkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
mInterface = null;
}
@Override
public void startLegacyVpnRunner() {
mStartLegacyVpnCv.open();
}
public void expectStartLegacyVpnRunner() {
assertTrue("startLegacyVpnRunner not called after " + TIMEOUT_MS + " ms",
mStartLegacyVpnCv.block(TIMEOUT_MS));
// startLegacyVpn calls stopVpnRunnerPrivileged, which will open mStopVpnRunnerCv, just
// before calling startLegacyVpnRunner. Restore mStopVpnRunnerCv, so the test can expect
// that the VpnRunner is stopped and immediately restarted by calling
// expectStartLegacyVpnRunner() and expectStopVpnRunnerPrivileged() back-to-back.
mStopVpnRunnerCv = new ConditionVariable();
}
@Override
public void stopVpnRunnerPrivileged() {
if (mVpnRunner != null) {
super.stopVpnRunnerPrivileged();
disconnect();
mStartLegacyVpnCv = new ConditionVariable();
}
mVpnRunner = null;
mStopVpnRunnerCv.open();
}
public void expectStopVpnRunnerPrivileged() {
assertTrue("stopVpnRunnerPrivileged not called after " + TIMEOUT_MS + " ms",
mStopVpnRunnerCv.block(TIMEOUT_MS));
}
@Override
public synchronized VpnInfo getVpnInfo() {
if (mVpnInfo != null) return mVpnInfo;
@@ -1271,10 +1319,19 @@ public class ConnectivityServiceTest {
}
}
private static final int VPN_USER = 0;
private static final int APP1_UID = UserHandle.getUid(VPN_USER, 10100);
private static final int APP2_UID = UserHandle.getUid(VPN_USER, 10101);
private static final int VPN_UID = UserHandle.getUid(VPN_USER, 10043);
private static final int PRIMARY_USER = 0;
private static final int APP1_UID = UserHandle.getUid(PRIMARY_USER, 10100);
private static final int APP2_UID = UserHandle.getUid(PRIMARY_USER, 10101);
private static final int VPN_UID = UserHandle.getUid(PRIMARY_USER, 10043);
private static final UserInfo PRIMARY_USER_INFO = new UserInfo(PRIMARY_USER, "",
UserInfo.FLAG_PRIMARY);
private static final int RESTRICTED_USER = 1;
private static final UserInfo RESTRICTED_USER_INFO = new UserInfo(RESTRICTED_USER, "",
UserInfo.FLAG_RESTRICTED);
static {
RESTRICTED_USER_INFO.restrictedProfileParentId = PRIMARY_USER;
}
@Before
public void setUp() throws Exception {
@@ -1284,10 +1341,13 @@ public class ConnectivityServiceTest {
MockitoAnnotations.initMocks(this);
when(mUserManager.getAliveUsers()).thenReturn(
Arrays.asList(new UserInfo[] {
new UserInfo(VPN_USER, "", 0),
}));
when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO));
when(mUserManager.getUserInfo(PRIMARY_USER)).thenReturn(PRIMARY_USER_INFO);
// canHaveRestrictedProfile does not take a userId. It applies to the userId of the context
// it was started from, i.e., PRIMARY_USER.
when(mUserManager.canHaveRestrictedProfile()).thenReturn(true);
when(mUserManager.getUserInfo(RESTRICTED_USER)).thenReturn(RESTRICTED_USER_INFO);
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
@@ -1329,6 +1389,9 @@ public class ConnectivityServiceTest {
verify(mNetworkPolicyManager).registerListener(policyListenerCaptor.capture());
mPolicyListener = policyListenerCaptor.getValue();
mServiceContext.setPermission(
Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED);
// Create local CM before sending system ready so that we can answer
// getSystemService() correctly.
mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService);
@@ -1359,6 +1422,7 @@ public class ConnectivityServiceTest {
doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt());
doReturn(mBatteryStatsService).when(deps).getBatteryStatsService();
doReturn(mKeyStore).when(deps).getKeyStore();
doAnswer(inv -> {
mPolicyTracker = new WrappedMultinetworkPolicyTracker(
inv.getArgument(0), inv.getArgument(1), inv.getArgument(2));
@@ -1524,10 +1588,9 @@ public class ConnectivityServiceTest {
}
public void expectNoBroadcast(int timeoutMs) throws Exception {
waitForIdle();
try {
final Intent intent = get(timeoutMs, TimeUnit.MILLISECONDS);
fail("Unexpected broadcast: " + intent.getAction());
fail("Unexpected broadcast: " + intent.getAction() + " " + intent.getExtras());
} catch (TimeoutException expected) {
} finally {
mServiceContext.unregisterReceiver(mReceiver);
@@ -6309,7 +6372,7 @@ public class ConnectivityServiceTest {
}
@Test
public void testVpnRestrictedUsers() throws Exception {
public void testRestrictedProfileAffectsVpnUidRanges() throws Exception {
// NETWORK_SETTINGS is necessary to see the UID ranges in NetworkCapabilities.
mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS,
PERMISSION_GRANTED);
@@ -6341,19 +6404,11 @@ public class ConnectivityServiceTest {
callback.expectCapabilitiesThat(mWiFiNetworkAgent, (caps)
-> caps.hasCapability(NET_CAPABILITY_VALIDATED));
// Create a fake restricted profile whose parent is our user ID.
final int userId = UserHandle.getUserId(uid);
when(mUserManager.canHaveRestrictedProfile()).thenReturn(true);
final int restrictedUserId = userId + 1;
final UserInfo info = new UserInfo(restrictedUserId, "user", UserInfo.FLAG_RESTRICTED);
info.restrictedProfileParentId = userId;
assertTrue(info.isRestricted());
when(mUserManager.getUserInfo(restrictedUserId)).thenReturn(info);
when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, restrictedUserId))
.thenReturn(UserHandle.getUid(restrictedUserId, VPN_UID));
when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, RESTRICTED_USER))
.thenReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID));
final Intent addedIntent = new Intent(ACTION_USER_ADDED);
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, restrictedUserId);
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
// Send a USER_ADDED broadcast for it.
// The BroadcastReceiver for this broadcast checks that is being run on the handler thread.
@@ -6365,7 +6420,7 @@ public class ConnectivityServiceTest {
callback.expectCapabilitiesThat(mMockVpn, (caps)
-> caps.getUids().size() == 2
&& caps.getUids().contains(new UidRange(uid, uid))
&& caps.getUids().contains(UidRange.createForUser(restrictedUserId))
&& caps.getUids().contains(UidRange.createForUser(RESTRICTED_USER))
&& caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_WIFI));
@@ -6375,13 +6430,13 @@ public class ConnectivityServiceTest {
callback.expectCapabilitiesThat(mMockVpn, (caps)
-> caps.getUids().size() == 2
&& caps.getUids().contains(new UidRange(uid, uid))
&& caps.getUids().contains(UidRange.createForUser(restrictedUserId))
&& caps.getUids().contains(UidRange.createForUser(RESTRICTED_USER))
&& caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_WIFI));
// Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, restrictedUserId);
removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
// Expect that the VPN gains the UID range for the restricted user, and that the capability
@@ -6391,53 +6446,68 @@ public class ConnectivityServiceTest {
&& caps.getUids().contains(new UidRange(uid, uid))
&& caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_WIFI));
}
// Test lockdown with restricted profiles.
@Test
public void testLockdownVpnWithRestrictedProfiles() throws Exception {
// NETWORK_SETTINGS is necessary to see the UID ranges in NetworkCapabilities.
mServiceContext.setPermission(
Manifest.permission.CONTROL_ALWAYS_ON_VPN, PERMISSION_GRANTED);
mServiceContext.setPermission(
Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED);
mServiceContext.setPermission(
Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
final NetworkRequest request = new NetworkRequest.Builder()
.removeCapability(NET_CAPABILITY_NOT_VPN)
.build();
final TestNetworkCallback callback = new TestNetworkCallback();
mCm.registerNetworkCallback(request, callback);
final int uid = Process.myUid();
// Connect wifi and check that UIDs in the main and restricted profiles have network access.
mMockVpn.disconnect();
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true /* validated */);
final int restrictedUid = UserHandle.getUid(restrictedUserId, 42 /* appId */);
final int restrictedUid = UserHandle.getUid(RESTRICTED_USER, 42 /* appId */);
assertNotNull(mCm.getActiveNetworkForUid(uid));
assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
// Enable always-on VPN lockdown. The main user loses network access because no VPN is up.
final ArrayList<String> allowList = new ArrayList<>();
mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
mService.setAlwaysOnVpnPackage(PRIMARY_USER, ALWAYS_ON_PACKAGE, true /* lockdown */,
allowList);
waitForIdle();
assertNull(mCm.getActiveNetworkForUid(uid));
// This is arguably overspecified: a UID that is not running doesn't have an active network.
// But it's useful to check that non-default users do not lose network access, and to prove
// that the loss of connectivity below is indeed due to the restricted profile coming up.
assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
// Start the restricted profile, and check that the UID within it loses network access.
when(mUserManager.getAliveUsers()).thenReturn(
Arrays.asList(new UserInfo[] {
new UserInfo(userId, "", 0),
info
}));
when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, RESTRICTED_USER))
.thenReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID));
when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO,
RESTRICTED_USER_INFO));
// TODO: check that VPN app within restricted profile still has access, etc.
final Intent addedIntent = new Intent(ACTION_USER_ADDED);
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
final Handler handler = new Handler(mCsHandlerThread.getLooper());
handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
waitForIdle();
assertNull(mCm.getActiveNetworkForUid(uid));
assertNull(mCm.getActiveNetworkForUid(restrictedUid));
// Stop the restricted profile, and check that the UID within it has network access again.
when(mUserManager.getAliveUsers()).thenReturn(
Arrays.asList(new UserInfo[] {
new UserInfo(userId, "", 0),
}));
when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO));
// Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
waitForIdle();
assertNull(mCm.getActiveNetworkForUid(uid));
assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
mService.setAlwaysOnVpnPackage(PRIMARY_USER, null, false /* lockdown */, allowList);
waitForIdle();
}
@@ -6778,6 +6848,7 @@ public class ConnectivityServiceTest {
final int userId = UserHandle.getUserId(uid);
final ArrayList<String> allowList = new ArrayList<>();
mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
waitForIdle();
UidRangeParcel firstHalf = new UidRangeParcel(1, VPN_UID - 1);
UidRangeParcel secondHalf = new UidRangeParcel(VPN_UID + 1, 99999);
@@ -6799,10 +6870,10 @@ public class ConnectivityServiceTest {
// Disable lockdown, expect to see the network unblocked.
mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
expectNetworkRejectNonSecureVpn(inOrder, false, firstHalf, secondHalf);
callback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
vpnUidCallback.assertNoCallback();
expectNetworkRejectNonSecureVpn(inOrder, false, firstHalf, secondHalf);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
@@ -6845,9 +6916,11 @@ public class ConnectivityServiceTest {
// Disable lockdown, remove our UID from the allowlist, and re-enable lockdown.
// Everything should now be blocked.
mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
waitForIdle();
expectNetworkRejectNonSecureVpn(inOrder, false, piece1, piece2, piece3);
allowList.clear();
mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
waitForIdle();
expectNetworkRejectNonSecureVpn(inOrder, true, firstHalf, secondHalf);
defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent);
assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent);
@@ -6925,6 +6998,194 @@ public class ConnectivityServiceTest {
mCm.unregisterNetworkCallback(vpnUidCallback);
}
private void setupLegacyLockdownVpn() {
final String profileName = "testVpnProfile";
final byte[] profileTag = profileName.getBytes(StandardCharsets.UTF_8);
when(mKeyStore.contains(Credentials.LOCKDOWN_VPN)).thenReturn(true);
when(mKeyStore.get(Credentials.LOCKDOWN_VPN)).thenReturn(profileTag);
final VpnProfile profile = new VpnProfile(profileName);
profile.name = "My VPN";
profile.server = "192.0.2.1";
profile.dnsServers = "8.8.8.8";
profile.type = VpnProfile.TYPE_IPSEC_XAUTH_PSK;
final byte[] encodedProfile = profile.encode();
when(mKeyStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile);
}
@Test
public void testLegacyLockdownVpn() throws Exception {
final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
final TestNetworkCallback callback = new TestNetworkCallback();
mCm.registerNetworkCallback(request, callback);
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback);
// Pretend lockdown VPN was configured.
setupLegacyLockdownVpn();
// LockdownVpnTracker disables the Vpn teardown code and enables lockdown.
// Check the VPN's state before it does so.
assertTrue(mMockVpn.getEnableTeardown());
assertFalse(mMockVpn.getLockdown());
// Send a USER_UNLOCKED broadcast so CS starts LockdownVpnTracker.
final int userId = UserHandle.getUserId(Process.myUid());
final Intent addedIntent = new Intent(ACTION_USER_UNLOCKED);
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
final Handler handler = new Handler(mCsHandlerThread.getLooper());
handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
waitForIdle();
// Lockdown VPN disables teardown and enables lockdown.
assertFalse(mMockVpn.getEnableTeardown());
assertTrue(mMockVpn.getLockdown());
// Bring up a network.
// Expect nothing to happen because the network does not have an IPv4 default route: legacy
// VPN only supports IPv4.
final LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName("rmnet0");
cellLp.addLinkAddress(new LinkAddress("2001:db8::1/64"));
cellLp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, "rmnet0"));
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
mCellNetworkAgent.connect(false /* validated */);
callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
waitForIdle();
assertNull(mMockVpn.getAgent());
// Add an IPv4 address. Ideally the VPN should start, but it doesn't because nothing calls
// LockdownVpnTracker#handleStateChangedLocked. This is a bug.
// TODO: consider fixing this.
cellLp.addLinkAddress(new LinkAddress("192.0.2.2/25"));
cellLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "rmnet0"));
mCellNetworkAgent.sendLinkProperties(cellLp);
callback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
defaultCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
waitForIdle();
assertNull(mMockVpn.getAgent());
// Disconnect, then try again with a network that supports IPv4 at connection time.
// Expect lockdown VPN to come up.
ExpectedBroadcast b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED);
mCellNetworkAgent.disconnect();
callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
b1.expectBroadcast();
// When lockdown VPN is active, the NetworkInfo state in CONNECTIVITY_ACTION is overwritten
// with the state of the VPN network. So expect a CONNECTING broadcast.
b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTING);
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
mCellNetworkAgent.connect(false /* validated */);
callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
b1.expectBroadcast();
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED);
// TODO: it would be nice if we could simply rely on the production code here, and have
// LockdownVpnTracker start the VPN, have the VPN code register its NetworkAgent with
// ConnectivityService, etc. That would require duplicating a fair bit of code from the
// Vpn tests around how to mock out LegacyVpnRunner. But even if we did that, this does not
// work for at least two reasons:
// 1. In this test, calling registerNetworkAgent does not actually result in an agent being
// registered. This is because nothing calls onNetworkMonitorCreated, which is what
// actually ends up causing handleRegisterNetworkAgent to be called. Code in this test
// that wants to register an agent must use TestNetworkAgentWrapper.
// 2. Even if we exposed Vpn#agentConnect to the test, and made MockVpn#agentConnect call
// the TestNetworkAgentWrapper code, this would deadlock because the
// TestNetworkAgentWrapper code cannot be called on the handler thread since it calls
// waitForIdle().
mMockVpn.expectStartLegacyVpnRunner();
b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
ExpectedBroadcast b2 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED);
mMockVpn.establishForMyUid();
callback.expectAvailableThenValidatedCallbacks(mMockVpn);
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
b1.expectBroadcast();
b2.expectBroadcast();
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
// Switch default network from cell to wifi. Expect VPN to disconnect and reconnect.
final LinkProperties wifiLp = new LinkProperties();
wifiLp.setInterfaceName("wlan0");
wifiLp.addLinkAddress(new LinkAddress("192.0.2.163/25"));
wifiLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "wlan0"));
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED);
// Wifi is CONNECTING because the VPN isn't up yet.
b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTING);
ExpectedBroadcast b3 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED);
mWiFiNetworkAgent.connect(false /* validated */);
b1.expectBroadcast();
b2.expectBroadcast();
b3.expectBroadcast();
mMockVpn.expectStopVpnRunnerPrivileged();
mMockVpn.expectStartLegacyVpnRunner();
// TODO: why is wifi not blocked? Is this because something calls prepare()?
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
callback.expectCapabilitiesThat(mMockVpn, (nc) -> nc.hasTransport(TRANSPORT_WIFI));
defaultCallback.expectCapabilitiesThat(mMockVpn, (nc) -> nc.hasTransport(TRANSPORT_WIFI));
callback.expectCallback(CallbackEntry.LOST, mMockVpn);
defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent);
// While the VPN is reconnecting on the new network, everything is blocked.
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED);
// The VPN comes up again on wifi.
b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
mMockVpn.establishForMyUid();
callback.expectAvailableThenValidatedCallbacks(mMockVpn);
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
b1.expectBroadcast();
b2.expectBroadcast();
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
// Disconnect cell. Nothing much happens since it's not the default network.
// Whenever LockdownVpnTracker is connected, it will send a connected broadcast any time any
// NetworkInfo is updated. This is probably a bug.
// TODO: consider fixing this.
b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
mCellNetworkAgent.disconnect();
b1.expectBroadcast();
callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
defaultCallback.assertNoCallback();
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED);
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
b1.expectBroadcast();
callback.expectCapabilitiesThat(mMockVpn, (nc) -> !nc.hasTransport(TRANSPORT_WIFI));
b2 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED);
mMockVpn.expectStopVpnRunnerPrivileged();
callback.expectCallback(CallbackEntry.LOST, mMockVpn);
b2.expectBroadcast();
}
@Test
public final void testLoseTrusted() throws Exception {
final NetworkRequest trustedRequest = new NetworkRequest.Builder()
@@ -7599,7 +7860,7 @@ public class ConnectivityServiceTest {
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
mMockVpn.establish(lp, VPN_UID, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, VPN_UID);
@@ -7627,7 +7888,7 @@ public class ConnectivityServiceTest {
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID);
@@ -7643,7 +7904,7 @@ public class ConnectivityServiceTest {
lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun0"));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID);
@@ -7658,7 +7919,7 @@ public class ConnectivityServiceTest {
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
mMockVpn.establish(lp, VPN_UID, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, VPN_UID);
@@ -7710,7 +7971,7 @@ public class ConnectivityServiceTest {
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final UidRange vpnRange = UidRange.createForUser(VPN_USER);
final UidRange vpnRange = UidRange.createForUser(PRIMARY_USER);
final Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
mMockVpn.establish(lp, VPN_UID, vpnRanges);
assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
@@ -7908,7 +8169,7 @@ public class ConnectivityServiceTest {
private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
throws Exception {
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid);
mMockVpn.setVpnType(vpnType);
@@ -8477,7 +8738,7 @@ public class ConnectivityServiceTest {
lp.setInterfaceName("tun0");
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
final UidRange vpnRange = UidRange.createForUser(VPN_USER);
final UidRange vpnRange = UidRange.createForUser(PRIMARY_USER);
Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
mMockVpn.establish(lp, VPN_UID, vpnRanges);
assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);