Convert LockdownVpnTracker to NetworkCallbacks.

This will allow moving LockdownVpnTracker from the connectivity
to the VPN code. This requires moderate refactoring since it's
pretty tightly coupled to both.

In this CL:

1. Add an @hide API to tell ConnectivityService that legacy
   lockdown VPN is enabled. I chose not to use the existing
   setVpnRequiredForUids API because that method has specific
   semantics and because it will be required long term since
   it's used by non-legacy VPN types.

2. Instead of updating LockdownVpnTracker inline from the
   ConnectivityService handler thread, have it listen to
   NetworkCallbacks. This introduces an extra thread hop, but
   most of the interactions between the lockdown VPN and CS were
   via NetworkAgent, which is asynchronous anyway.

3. Add code to LegacyTypeTracker to send the extra
   CONNECTIVITY_ACTION broadcast for the underlying network type
   that is sent after the VPN connects. In order to do this, make
   Make LockdownVpnTracker specify its underlying network
   (via setUnderlyingNetworks) when it connects.

4. Reimplement LockdownVpnTracker#augmentNetworkInfo based on
   information that is available in ConnectivityService.

5. Remove the code in LockdownVpnTracker that counted errors.
   I think this code has not worked since lollipop, because
   ConnectivityService never sees NetworkInfo objects in state
   FAILED. This is because ConnectivityService only hears about
   NetworkInfo objects via NetworkAgents, and LegacyVpnRunner
   only registers its NetworkAgent when the connection succeeds.

Bug: 173331190
Test: passes existing tests in ConnectivityServiceTest
Change-Id: I66d18512882efd468ee0ecec61f28786a195b357
This commit is contained in:
Lorenzo Colitti
2021-01-15 01:29:01 +09:00
parent 9f38060e30
commit bcd692fdc9
4 changed files with 180 additions and 81 deletions

View File

@@ -7339,11 +7339,14 @@ public class ConnectivityServiceTest {
when(mKeyStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile);
}
private void establishLegacyLockdownVpn() throws Exception {
private void establishLegacyLockdownVpn(Network underlying) throws Exception {
// The legacy lockdown VPN only supports userId 0, and must have an underlying network.
assertNotNull(underlying);
mMockVpn.setVpnType(VpnManager.TYPE_VPN_LEGACY);
// The legacy lockdown VPN only supports userId 0.
final Set<UidRange> ranges = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
mMockVpn.registerAgent(ranges);
mMockVpn.setUnderlyingNetworks(new Network[]{underlying});
mMockVpn.connect(true);
}
@@ -7351,6 +7354,9 @@ public class ConnectivityServiceTest {
public void testLegacyLockdownVpn() throws Exception {
mServiceContext.setPermission(
Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED);
// For LockdownVpnTracker to call registerSystemDefaultNetworkCallback.
mServiceContext.setPermission(
Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
final TestNetworkCallback callback = new TestNetworkCallback();
@@ -7359,6 +7365,10 @@ public class ConnectivityServiceTest {
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback);
final TestNetworkCallback systemDefaultCallback = new TestNetworkCallback();
mCm.registerSystemDefaultNetworkCallback(systemDefaultCallback,
new Handler(ConnectivityThread.getInstanceLooper()));
// Pretend lockdown VPN was configured.
setupLegacyLockdownVpn();
@@ -7388,6 +7398,7 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.connect(false /* validated */);
callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
systemDefaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
waitForIdle();
assertNull(mMockVpn.getAgent());
@@ -7399,6 +7410,8 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.sendLinkProperties(cellLp);
callback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
defaultCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
systemDefaultCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
mCellNetworkAgent);
waitForIdle();
assertNull(mMockVpn.getAgent());
@@ -7408,6 +7421,7 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.disconnect();
callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
systemDefaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
b1.expectBroadcast();
// When lockdown VPN is active, the NetworkInfo state in CONNECTIVITY_ACTION is overwritten
@@ -7417,6 +7431,7 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.connect(false /* validated */);
callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
systemDefaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
b1.expectBroadcast();
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
@@ -7439,9 +7454,10 @@ public class ConnectivityServiceTest {
mMockVpn.expectStartLegacyVpnRunner();
b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
ExpectedBroadcast b2 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED);
establishLegacyLockdownVpn();
establishLegacyLockdownVpn(mCellNetworkAgent.getNetwork());
callback.expectAvailableThenValidatedCallbacks(mMockVpn);
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
systemDefaultCallback.assertNoCallback();
NetworkCapabilities vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
b1.expectBroadcast();
b2.expectBroadcast();
@@ -7453,9 +7469,7 @@ public class ConnectivityServiceTest {
assertTrue(vpnNc.hasTransport(TRANSPORT_CELLULAR));
assertFalse(vpnNc.hasTransport(TRANSPORT_WIFI));
assertFalse(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED));
VpnTransportInfo ti = (VpnTransportInfo) vpnNc.getTransportInfo();
assertNotNull(ti);
assertEquals(VpnManager.TYPE_VPN_LEGACY, ti.type);
assertVpnTransportInfo(vpnNc, VpnManager.TYPE_VPN_LEGACY);
// Switch default network from cell to wifi. Expect VPN to disconnect and reconnect.
final LinkProperties wifiLp = new LinkProperties();
@@ -7483,11 +7497,10 @@ public class ConnectivityServiceTest {
// fact that a VPN is connected should only result in the VPN itself being unblocked, not
// any other network. Bug in isUidBlockedByVpn?
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
callback.expectCapabilitiesThat(mMockVpn, nc -> nc.hasTransport(TRANSPORT_WIFI));
callback.expectCallback(CallbackEntry.LOST, mMockVpn);
defaultCallback.expectCapabilitiesThat(mMockVpn, nc -> nc.hasTransport(TRANSPORT_WIFI));
defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent);
systemDefaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// While the VPN is reconnecting on the new network, everything is blocked.
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
@@ -7498,9 +7511,10 @@ public class ConnectivityServiceTest {
// The VPN comes up again on wifi.
b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
establishLegacyLockdownVpn();
establishLegacyLockdownVpn(mWiFiNetworkAgent.getNetwork());
callback.expectAvailableThenValidatedCallbacks(mMockVpn);
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
systemDefaultCallback.assertNoCallback();
b1.expectBroadcast();
b2.expectBroadcast();
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
@@ -7514,14 +7528,10 @@ public class ConnectivityServiceTest {
assertTrue(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED));
// 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();
systemDefaultCallback.assertNoCallback();
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
@@ -7531,6 +7541,7 @@ public class ConnectivityServiceTest {
b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED);
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
systemDefaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
b1.expectBroadcast();
callback.expectCapabilitiesThat(mMockVpn, nc -> !nc.hasTransport(TRANSPORT_WIFI));
b2 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED);