Merge "Add underlying networks into NetworkAgentInfo if any"

This commit is contained in:
Lucas Lin
2021-10-21 08:33:29 +00:00
committed by Gerrit Code Review
4 changed files with 155 additions and 43 deletions

View File

@@ -151,6 +151,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import static org.mockito.AdditionalMatchers.aryEq;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -319,6 +320,7 @@ import com.android.internal.net.VpnProfile;
import com.android.internal.util.WakeupMessage;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.ArrayTrackRecord;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.LocationPermissionChecker;
@@ -7252,6 +7254,15 @@ public class ConnectivityServiceTest {
initialCaps.addTransportType(TRANSPORT_VPN);
initialCaps.addCapability(NET_CAPABILITY_INTERNET);
initialCaps.removeCapability(NET_CAPABILITY_NOT_VPN);
final ArrayList<Network> emptyUnderlyingNetworks = new ArrayList<Network>();
final ArrayList<Network> underlyingNetworksContainMobile = new ArrayList<Network>();
underlyingNetworksContainMobile.add(mobile);
final ArrayList<Network> underlyingNetworksContainWifi = new ArrayList<Network>();
underlyingNetworksContainWifi.add(wifi);
final ArrayList<Network> underlyingNetworksContainMobileAndMobile =
new ArrayList<Network>();
underlyingNetworksContainMobileAndMobile.add(mobile);
underlyingNetworksContainMobileAndMobile.add(wifi);
final NetworkCapabilities withNoUnderlying = new NetworkCapabilities();
withNoUnderlying.addCapability(NET_CAPABILITY_INTERNET);
@@ -7260,17 +7271,20 @@ public class ConnectivityServiceTest {
withNoUnderlying.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
withNoUnderlying.addTransportType(TRANSPORT_VPN);
withNoUnderlying.removeCapability(NET_CAPABILITY_NOT_VPN);
withNoUnderlying.setUnderlyingNetworks(emptyUnderlyingNetworks);
final NetworkCapabilities withMobileUnderlying = new NetworkCapabilities(withNoUnderlying);
withMobileUnderlying.addTransportType(TRANSPORT_CELLULAR);
withMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_ROAMING);
withMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
withMobileUnderlying.setLinkDownstreamBandwidthKbps(10);
withMobileUnderlying.setUnderlyingNetworks(underlyingNetworksContainMobile);
final NetworkCapabilities withWifiUnderlying = new NetworkCapabilities(withNoUnderlying);
withWifiUnderlying.addTransportType(TRANSPORT_WIFI);
withWifiUnderlying.addCapability(NET_CAPABILITY_NOT_METERED);
withWifiUnderlying.setLinkUpstreamBandwidthKbps(20);
withWifiUnderlying.setUnderlyingNetworks(underlyingNetworksContainWifi);
final NetworkCapabilities withWifiAndMobileUnderlying =
new NetworkCapabilities(withNoUnderlying);
@@ -7280,6 +7294,7 @@ public class ConnectivityServiceTest {
withWifiAndMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_ROAMING);
withWifiAndMobileUnderlying.setLinkDownstreamBandwidthKbps(10);
withWifiAndMobileUnderlying.setLinkUpstreamBandwidthKbps(20);
withWifiAndMobileUnderlying.setUnderlyingNetworks(underlyingNetworksContainMobileAndMobile);
final NetworkCapabilities initialCapsNotMetered = new NetworkCapabilities(initialCaps);
initialCapsNotMetered.addCapability(NET_CAPABILITY_NOT_METERED);
@@ -7287,40 +7302,61 @@ public class ConnectivityServiceTest {
NetworkCapabilities caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{}, initialCapsNotMetered, caps);
assertEquals(withNoUnderlying, caps);
assertEquals(0, new ArrayList<>(caps.getUnderlyingNetworks()).size());
caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{null}, initialCapsNotMetered, caps);
assertEquals(withNoUnderlying, caps);
assertEquals(0, new ArrayList<>(caps.getUnderlyingNetworks()).size());
caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{mobile}, initialCapsNotMetered, caps);
assertEquals(withMobileUnderlying, caps);
assertEquals(1, new ArrayList<>(caps.getUnderlyingNetworks()).size());
assertEquals(mobile, new ArrayList<>(caps.getUnderlyingNetworks()).get(0));
caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{wifi}, initialCapsNotMetered, caps);
assertEquals(withWifiUnderlying, caps);
assertEquals(1, new ArrayList<>(caps.getUnderlyingNetworks()).size());
assertEquals(wifi, new ArrayList<>(caps.getUnderlyingNetworks()).get(0));
withWifiUnderlying.removeCapability(NET_CAPABILITY_NOT_METERED);
caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{wifi}, initialCaps, caps);
assertEquals(withWifiUnderlying, caps);
assertEquals(1, new ArrayList<>(caps.getUnderlyingNetworks()).size());
assertEquals(wifi, new ArrayList<>(caps.getUnderlyingNetworks()).get(0));
caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{mobile, wifi}, initialCaps, caps);
assertEquals(withWifiAndMobileUnderlying, caps);
assertEquals(2, new ArrayList<>(caps.getUnderlyingNetworks()).size());
assertEquals(mobile, new ArrayList<>(caps.getUnderlyingNetworks()).get(0));
assertEquals(wifi, new ArrayList<>(caps.getUnderlyingNetworks()).get(1));
withWifiUnderlying.addCapability(NET_CAPABILITY_NOT_METERED);
caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi},
initialCapsNotMetered, caps);
assertEquals(withWifiAndMobileUnderlying, caps);
assertEquals(2, new ArrayList<>(caps.getUnderlyingNetworks()).size());
assertEquals(mobile, new ArrayList<>(caps.getUnderlyingNetworks()).get(0));
assertEquals(wifi, new ArrayList<>(caps.getUnderlyingNetworks()).get(1));
caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi},
initialCapsNotMetered, caps);
assertEquals(withWifiAndMobileUnderlying, caps);
assertEquals(2, new ArrayList<>(caps.getUnderlyingNetworks()).size());
assertEquals(mobile, new ArrayList<>(caps.getUnderlyingNetworks()).get(0));
assertEquals(wifi, new ArrayList<>(caps.getUnderlyingNetworks()).get(1));
caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(null, initialCapsNotMetered, caps);
assertEquals(withWifiUnderlying, caps);
assertEquals(1, new ArrayList<>(caps.getUnderlyingNetworks()).size());
assertEquals(wifi, new ArrayList<>(caps.getUnderlyingNetworks()).get(0));
}
@Test
@@ -7329,51 +7365,78 @@ public class ConnectivityServiceTest {
final NetworkRequest request = new NetworkRequest.Builder()
.removeCapability(NET_CAPABILITY_NOT_VPN).build();
mCm.registerNetworkCallback(request, callback);
runAsShell(NETWORK_SETTINGS, () -> {
mCm.registerNetworkCallback(request, callback);
// Bring up a VPN that specifies an underlying network that does not exist yet.
// Note: it's sort of meaningless for a VPN app to declare a network that doesn't exist yet,
// (and doing so is difficult without using reflection) but it's good to test that the code
// behaves approximately correctly.
mMockVpn.establishForMyUid(false, true, false);
assertUidRangesUpdatedForMyUid(true);
final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId());
mMockVpn.setUnderlyingNetworks(new Network[]{wifiNetwork});
callback.expectAvailableCallbacksUnvalidated(mMockVpn);
assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
.hasTransport(TRANSPORT_VPN));
assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
.hasTransport(TRANSPORT_WIFI));
// Bring up a VPN that specifies an underlying network that does not exist yet.
// Note: it's sort of meaningless for a VPN app to declare a network that doesn't exist
// yet, (and doing so is difficult without using reflection) but it's good to test that
// the code behaves approximately correctly.
mMockVpn.establishForMyUid(false, true, false);
callback.expectAvailableCallbacksUnvalidated(mMockVpn);
assertUidRangesUpdatedForMyUid(true);
final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId());
mMockVpn.setUnderlyingNetworks(new Network[]{wifiNetwork});
// onCapabilitiesChanged() should be called because
// NetworkCapabilities#mUnderlyingNetworks is updated.
CallbackEntry ce = callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED,
mMockVpn);
final NetworkCapabilities vpnNc1 = ((CallbackEntry.CapabilitiesChanged) ce).getCaps();
// Since the wifi network hasn't brought up,
// ConnectivityService#applyUnderlyingCapabilities cannot find it. Update
// NetworkCapabilities#mUnderlyingNetworks to an empty array, and it will be updated to
// the correct underlying networks once the wifi network brings up. But this case
// shouldn't happen in reality since no one could get the network which hasn't brought
// up. For the empty array of underlying networks, it should be happened for 2 cases,
// the first one is that the VPN app declares an empty array for its underlying
// networks, the second one is that the underlying networks are torn down.
//
// It shouldn't be null since the null value means the underlying networks of this
// network should follow the default network.
final ArrayList<Network> underlyingNetwork = new ArrayList<>();
assertEquals(underlyingNetwork, vpnNc1.getUnderlyingNetworks());
// Since the wifi network isn't exist, applyUnderlyingCapabilities()
assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
.hasTransport(TRANSPORT_VPN));
assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
.hasTransport(TRANSPORT_WIFI));
// Make that underlying network connect, and expect to see its capabilities immediately
// reflected in the VPN's capabilities.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
assertEquals(wifiNetwork, mWiFiNetworkAgent.getNetwork());
mWiFiNetworkAgent.connect(false);
// TODO: the callback for the VPN happens before any callbacks are called for the wifi
// network that has just connected. There appear to be two issues here:
// 1. The VPN code will accept an underlying network as soon as getNetworkCapabilities() for
// it returns non-null (which happens very early, during handleRegisterNetworkAgent).
// This is not correct because that that point the network is not connected and cannot
// pass any traffic.
// 2. When a network connects, updateNetworkInfo propagates underlying network capabilities
// before rematching networks.
// Given that this scenario can't really happen, this is probably fine for now.
callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
.hasTransport(TRANSPORT_VPN));
assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
.hasTransport(TRANSPORT_WIFI));
// Make that underlying network connect, and expect to see its capabilities immediately
// reflected in the VPN's capabilities.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
assertEquals(wifiNetwork, mWiFiNetworkAgent.getNetwork());
mWiFiNetworkAgent.connect(false);
// TODO: the callback for the VPN happens before any callbacks are called for the wifi
// network that has just connected. There appear to be two issues here:
// 1. The VPN code will accept an underlying network as soon as getNetworkCapabilities()
// for it returns non-null (which happens very early, during
// handleRegisterNetworkAgent).
// This is not correct because that that point the network is not connected and
// cannot pass any traffic.
// 2. When a network connects, updateNetworkInfo propagates underlying network
// capabilities before rematching networks.
// Given that this scenario can't really happen, this is probably fine for now.
ce = callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
final NetworkCapabilities vpnNc2 = ((CallbackEntry.CapabilitiesChanged) ce).getCaps();
// The wifi network is brought up, NetworkCapabilities#mUnderlyingNetworks is updated to
// it.
underlyingNetwork.add(wifiNetwork);
assertEquals(underlyingNetwork, vpnNc2.getUnderlyingNetworks());
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
.hasTransport(TRANSPORT_VPN));
assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
.hasTransport(TRANSPORT_WIFI));
// Disconnect the network, and expect to see the VPN capabilities change accordingly.
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
callback.expectCapabilitiesThat(mMockVpn, (nc) ->
nc.getTransportTypes().length == 1 && nc.hasTransport(TRANSPORT_VPN));
// Disconnect the network, and expect to see the VPN capabilities change accordingly.
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
callback.expectCapabilitiesThat(mMockVpn, (nc) ->
nc.getTransportTypes().length == 1 && nc.hasTransport(TRANSPORT_VPN));
mMockVpn.disconnect();
mCm.unregisterNetworkCallback(callback);
mMockVpn.disconnect();
mCm.unregisterNetworkCallback(callback);
});
}
private void assertGetNetworkInfoOfGetActiveNetworkIsConnected(boolean expectedConnectivity) {
@@ -10697,6 +10760,14 @@ public class ConnectivityServiceTest {
return fakeNai(wifiNc, info);
}
private NetworkAgentInfo fakeVpnNai(NetworkCapabilities nc) {
final NetworkCapabilities vpnNc = new NetworkCapabilities.Builder(nc)
.addTransportType(TRANSPORT_VPN).build();
final NetworkInfo info = new NetworkInfo(TYPE_VPN, 0 /* subtype */,
ConnectivityManager.getNetworkTypeName(TYPE_VPN), "" /* subtypeName */);
return fakeNai(vpnNc, info);
}
private NetworkAgentInfo fakeNai(NetworkCapabilities nc, NetworkInfo networkInfo) {
return new NetworkAgentInfo(null, new Network(NET_ID), networkInfo, new LinkProperties(),
nc, new NetworkScore.Builder().setLegacyInt(0).build(),
@@ -10830,6 +10901,36 @@ public class ConnectivityServiceTest {
mContext.getOpPackageName()));
}
@Test
public void testUnderlyingNetworksWillBeSetInNetworkAgentInfoConstructor() throws Exception {
assumeTrue(SdkLevel.isAtLeastT());
final Network network1 = new Network(100);
final Network network2 = new Network(101);
final List<Network> underlyingNetworks = new ArrayList<>();
final NetworkCapabilities ncWithEmptyUnderlyingNetworks = new NetworkCapabilities.Builder()
.setUnderlyingNetworks(underlyingNetworks)
.build();
final NetworkAgentInfo vpnNaiWithEmptyUnderlyingNetworks =
fakeVpnNai(ncWithEmptyUnderlyingNetworks);
assertEquals(underlyingNetworks,
Arrays.asList(vpnNaiWithEmptyUnderlyingNetworks.declaredUnderlyingNetworks));
underlyingNetworks.add(network1);
underlyingNetworks.add(network2);
final NetworkCapabilities ncWithUnderlyingNetworks = new NetworkCapabilities.Builder()
.setUnderlyingNetworks(underlyingNetworks)
.build();
final NetworkAgentInfo vpnNaiWithUnderlyingNetwokrs = fakeVpnNai(ncWithUnderlyingNetworks);
assertEquals(underlyingNetworks,
Arrays.asList(vpnNaiWithUnderlyingNetwokrs.declaredUnderlyingNetworks));
final NetworkCapabilities ncWithoutUnderlyingNetworks = new NetworkCapabilities.Builder()
.build();
final NetworkAgentInfo vpnNaiWithoutUnderlyingNetwokrs =
fakeVpnNai(ncWithoutUnderlyingNetworks);
assertNull(vpnNaiWithoutUnderlyingNetwokrs.declaredUnderlyingNetworks);
}
@Test
public void testRegisterConnectivityDiagnosticsCallbackCallsOnConnectivityReport()
throws Exception {