Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 97d08c05 authored by Lorenzo Colitti's avatar Lorenzo Colitti Committed by Automerger Merge Worker
Browse files

Merge changes Ic5a3e169,I76daa3ab am: 8ca38568 am: 88f10e63 am: bb86b5e4

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1512888

Change-Id: If54ea6526d4426e6e52eec40c72f3ddd625842fe
parents 4e2aa36c bb86b5e4
Loading
Loading
Loading
Loading
+62 −8
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
@@ -6354,18 +6355,71 @@ public class ConnectivityService extends IConnectivityManager.Stub
        nai.declaredMetered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED);
    }

    /** Propagates to |nc| the capabilities declared by the underlying networks of |nai|. */
    private void mixInUnderlyingCapabilities(NetworkAgentInfo nai, NetworkCapabilities nc) {
        Network[] underlyingNetworks = nai.declaredUnderlyingNetworks;
        Network defaultNetwork = getNetwork(getDefaultNetwork());
    /** Modifies |caps| based on the capabilities of the specified underlying networks. */
    @VisibleForTesting
    void applyUnderlyingCapabilities(@Nullable Network[] underlyingNetworks,
            @NonNull NetworkCapabilities caps,  boolean declaredMetered) {
        final Network defaultNetwork = getNetwork(getDefaultNetwork());
        if (underlyingNetworks == null && defaultNetwork != null) {
            // null underlying networks means to track the default.
            underlyingNetworks = new Network[] { defaultNetwork };
        }

        // TODO(b/124469351): Get capabilities directly from ConnectivityService instead.
        final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
        Vpn.applyUnderlyingCapabilities(cm, underlyingNetworks, nc, nai.declaredMetered);
        int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN };
        int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
        int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
        boolean metered = declaredMetered; // metered if any underlying is metered, or agentMetered
        boolean roaming = false; // roaming if any underlying is roaming
        boolean congested = false; // congested if any underlying is congested
        boolean suspended = true; // suspended if all underlying are suspended

        boolean hadUnderlyingNetworks = false;
        if (null != underlyingNetworks) {
            for (Network underlyingNetwork : underlyingNetworks) {
                final NetworkAgentInfo underlying =
                        getNetworkAgentInfoForNetwork(underlyingNetwork);
                if (underlying == null) continue;

                final NetworkCapabilities underlyingCaps = underlying.networkCapabilities;
                hadUnderlyingNetworks = true;
                for (int underlyingType : underlyingCaps.getTransportTypes()) {
                    transportTypes = ArrayUtils.appendInt(transportTypes, underlyingType);
                }

                // Merge capabilities of this underlying network. For bandwidth, assume the
                // worst case.
                downKbps = NetworkCapabilities.minBandwidth(downKbps,
                        underlyingCaps.getLinkDownstreamBandwidthKbps());
                upKbps = NetworkCapabilities.minBandwidth(upKbps,
                        underlyingCaps.getLinkUpstreamBandwidthKbps());
                // If this underlying network is metered, the VPN is metered (it may cost money
                // to send packets on this network).
                metered |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_METERED);
                // If this underlying network is roaming, the VPN is roaming (the billing structure
                // is different than the usual, local one).
                roaming |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING);
                // If this underlying network is congested, the VPN is congested (the current
                // condition of the network affects the performance of this network).
                congested |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_CONGESTED);
                // If this network is not suspended, the VPN is not suspended (the VPN
                // is able to transfer some data).
                suspended &= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
            }
        }
        if (!hadUnderlyingNetworks) {
            // No idea what the underlying networks are; assume reasonable defaults
            metered = true;
            roaming = false;
            congested = false;
            suspended = false;
        }

        caps.setTransportTypes(transportTypes);
        caps.setLinkDownstreamBandwidthKbps(downKbps);
        caps.setLinkUpstreamBandwidthKbps(upKbps);
        caps.setCapability(NET_CAPABILITY_NOT_METERED, !metered);
        caps.setCapability(NET_CAPABILITY_NOT_ROAMING, !roaming);
        caps.setCapability(NET_CAPABILITY_NOT_CONGESTED, !congested);
        caps.setCapability(NET_CAPABILITY_NOT_SUSPENDED, !suspended);
    }

    /**
@@ -6422,7 +6476,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
        }

        if (nai.supportsUnderlyingNetworks()) {
            mixInUnderlyingCapabilities(nai, newNc);
            applyUnderlyingCapabilities(nai.declaredUnderlyingNetworks, newNc, nai.declaredMetered);
        }

        return newNc;
+0 −97
Original line number Diff line number Diff line
@@ -18,10 +18,7 @@ package com.android.server.connectivity;

import static android.Manifest.permission.BIND_VPN_SERVICE;
import static android.net.ConnectivityManager.NETID_UNSET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
import static android.net.RouteInfo.RTN_THROW;
import static android.net.RouteInfo.RTN_UNREACHABLE;

@@ -111,7 +108,6 @@ import com.android.internal.net.LegacyVpnInfo;
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.server.DeviceIdleInternal;
import com.android.server.LocalServices;
import com.android.server.net.BaseNetworkObserver;
@@ -462,99 +458,6 @@ public class Vpn {
        updateAlwaysOnNotification(detailedState);
    }

    /**
     * Updates {@link #mNetworkCapabilities} based on current underlying networks and returns a
     * defensive copy.
     *
     * <p>Does not propagate updated capabilities to apps.
     *
     * @param defaultNetwork underlying network for VPNs following platform's default
     */
    public synchronized NetworkCapabilities updateCapabilities(@Nullable Network defaultNetwork) {
        if (mConfig == null) {
            // VPN is not running.
            return null;
        }

        Network[] underlyingNetworks = mConfig.underlyingNetworks;
        if (underlyingNetworks == null && defaultNetwork != null) {
            // null underlying networks means to track the default.
            underlyingNetworks = new Network[] { defaultNetwork };
        }
        // Only apps targeting Q and above can explicitly declare themselves as metered.
        final boolean isAlwaysMetered = mIsPackageTargetingAtLeastQ && mConfig.isMetered;

        applyUnderlyingCapabilities(
                mConnectivityManager,
                underlyingNetworks,
                mNetworkCapabilities,
                isAlwaysMetered);

        return new NetworkCapabilities(mNetworkCapabilities);
    }

    @VisibleForTesting
    public static void applyUnderlyingCapabilities(
            @NonNull final ConnectivityManager cm,
            @Nullable final Network[] underlyingNetworks,
            @NonNull final NetworkCapabilities caps,
            final boolean isAlwaysMetered) {
        int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN };
        int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
        int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
        boolean metered = isAlwaysMetered; // metered if any underlying is metered, or alwaysMetered
        boolean roaming = false; // roaming if any underlying is roaming
        boolean congested = false; // congested if any underlying is congested
        boolean suspended = true; // suspended if all underlying are suspended

        boolean hadUnderlyingNetworks = false;
        if (null != underlyingNetworks) {
            for (Network underlying : underlyingNetworks) {
                // TODO(b/124469351): Get capabilities directly from ConnectivityService instead.
                final NetworkCapabilities underlyingCaps = cm.getNetworkCapabilities(underlying);
                if (underlyingCaps == null) continue;
                hadUnderlyingNetworks = true;
                for (int underlyingType : underlyingCaps.getTransportTypes()) {
                    transportTypes = ArrayUtils.appendInt(transportTypes, underlyingType);
                }

                // Merge capabilities of this underlying network. For bandwidth, assume the
                // worst case.
                downKbps = NetworkCapabilities.minBandwidth(downKbps,
                        underlyingCaps.getLinkDownstreamBandwidthKbps());
                upKbps = NetworkCapabilities.minBandwidth(upKbps,
                        underlyingCaps.getLinkUpstreamBandwidthKbps());
                // If this underlying network is metered, the VPN is metered (it may cost money
                // to send packets on this network).
                metered |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_METERED);
                // If this underlying network is roaming, the VPN is roaming (the billing structure
                // is different than the usual, local one).
                roaming |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING);
                // If this underlying network is congested, the VPN is congested (the current
                // condition of the network affects the performance of this network).
                congested |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_CONGESTED);
                // If this network is not suspended, the VPN is not suspended (the VPN
                // is able to transfer some data).
                suspended &= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
            }
        }
        if (!hadUnderlyingNetworks) {
            // No idea what the underlying networks are; assume the safer defaults
            metered = true;
            roaming = false;
            congested = false;
            suspended = false;
        }

        caps.setTransportTypes(transportTypes);
        caps.setLinkDownstreamBandwidthKbps(downKbps);
        caps.setLinkUpstreamBandwidthKbps(upKbps);
        caps.setCapability(NET_CAPABILITY_NOT_METERED, !metered);
        caps.setCapability(NET_CAPABILITY_NOT_ROAMING, !roaming);
        caps.setCapability(NET_CAPABILITY_NOT_CONGESTED, !congested);
        caps.setCapability(NET_CAPABILITY_NOT_SUSPENDED, !suspended);
    }

    /**
     * Chooses whether to force all connections to go though VPN.
     *
+115 −2
Original line number Diff line number Diff line
@@ -56,8 +56,10 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_IA;
import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
@@ -5397,6 +5399,106 @@ public class ConnectivityServiceTest {
        assertTrue(lp.getDnsServers().containsAll(dnsServers));
    }

    @Test
    public void testApplyUnderlyingCapabilities() throws Exception {
        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
        mCellNetworkAgent.connect(false /* validated */);
        mWiFiNetworkAgent.connect(false /* validated */);

        final NetworkCapabilities cellNc = new NetworkCapabilities()
                .addTransportType(TRANSPORT_CELLULAR)
                .addCapability(NET_CAPABILITY_INTERNET)
                .addCapability(NET_CAPABILITY_NOT_CONGESTED)
                .setLinkDownstreamBandwidthKbps(10);
        final NetworkCapabilities wifiNc = new NetworkCapabilities()
                .addTransportType(TRANSPORT_WIFI)
                .addCapability(NET_CAPABILITY_INTERNET)
                .addCapability(NET_CAPABILITY_NOT_METERED)
                .addCapability(NET_CAPABILITY_NOT_ROAMING)
                .addCapability(NET_CAPABILITY_NOT_CONGESTED)
                .addCapability(NET_CAPABILITY_NOT_SUSPENDED)
                .setLinkUpstreamBandwidthKbps(20);
        mCellNetworkAgent.setNetworkCapabilities(cellNc, true /* sendToConnectivityService */);
        mWiFiNetworkAgent.setNetworkCapabilities(wifiNc, true /* sendToConnectivityService */);
        waitForIdle();

        final Network mobile = mCellNetworkAgent.getNetwork();
        final Network wifi = mWiFiNetworkAgent.getNetwork();

        final NetworkCapabilities initialCaps = new NetworkCapabilities();
        initialCaps.addCapability(NET_CAPABILITY_INTERNET);
        initialCaps.removeCapability(NET_CAPABILITY_NOT_VPN);

        final NetworkCapabilities withNoUnderlying = new NetworkCapabilities();
        withNoUnderlying.addCapability(NET_CAPABILITY_INTERNET);
        withNoUnderlying.addCapability(NET_CAPABILITY_NOT_CONGESTED);
        withNoUnderlying.addCapability(NET_CAPABILITY_NOT_ROAMING);
        withNoUnderlying.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
        withNoUnderlying.addTransportType(TRANSPORT_VPN);
        withNoUnderlying.removeCapability(NET_CAPABILITY_NOT_VPN);

        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);

        final NetworkCapabilities withWifiUnderlying = new NetworkCapabilities(withNoUnderlying);
        withWifiUnderlying.addTransportType(TRANSPORT_WIFI);
        withWifiUnderlying.addCapability(NET_CAPABILITY_NOT_METERED);
        withWifiUnderlying.setLinkUpstreamBandwidthKbps(20);

        final NetworkCapabilities withWifiAndMobileUnderlying =
                new NetworkCapabilities(withNoUnderlying);
        withWifiAndMobileUnderlying.addTransportType(TRANSPORT_CELLULAR);
        withWifiAndMobileUnderlying.addTransportType(TRANSPORT_WIFI);
        withWifiAndMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_METERED);
        withWifiAndMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_ROAMING);
        withWifiAndMobileUnderlying.setLinkDownstreamBandwidthKbps(10);
        withWifiAndMobileUnderlying.setLinkUpstreamBandwidthKbps(20);

        NetworkCapabilities caps = new NetworkCapabilities(initialCaps);
        final boolean notDeclaredMetered = false;
        mService.applyUnderlyingCapabilities(new Network[]{}, caps, notDeclaredMetered);
        assertEquals(withNoUnderlying, caps);

        caps = new NetworkCapabilities(initialCaps);
        mService.applyUnderlyingCapabilities(new Network[]{null}, caps, notDeclaredMetered);
        assertEquals(withNoUnderlying, caps);

        caps = new NetworkCapabilities(initialCaps);
        mService.applyUnderlyingCapabilities(new Network[]{mobile}, caps, notDeclaredMetered);
        assertEquals(withMobileUnderlying, caps);

        mService.applyUnderlyingCapabilities(new Network[]{wifi}, caps, notDeclaredMetered);
        assertEquals(withWifiUnderlying, caps);

        final boolean isDeclaredMetered = true;
        withWifiUnderlying.removeCapability(NET_CAPABILITY_NOT_METERED);
        caps = new NetworkCapabilities(initialCaps);
        mService.applyUnderlyingCapabilities(new Network[]{wifi}, caps, isDeclaredMetered);
        assertEquals(withWifiUnderlying, caps);

        caps = new NetworkCapabilities(initialCaps);
        mService.applyUnderlyingCapabilities(new Network[]{mobile, wifi}, caps, isDeclaredMetered);
        assertEquals(withWifiAndMobileUnderlying, caps);

        withWifiUnderlying.addCapability(NET_CAPABILITY_NOT_METERED);
        caps = new NetworkCapabilities(initialCaps);
        mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi},
                caps, notDeclaredMetered);
        assertEquals(withWifiAndMobileUnderlying, caps);

        caps = new NetworkCapabilities(initialCaps);
        mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi},
                caps, notDeclaredMetered);
        assertEquals(withWifiAndMobileUnderlying, caps);

        mService.applyUnderlyingCapabilities(null, caps, notDeclaredMetered);
        assertEquals(withWifiUnderlying, caps);
    }

    @Test
    public void testVpnConnectDisconnectUnderlyingNetwork() throws Exception {
        final TestNetworkCallback callback = new TestNetworkCallback();
@@ -5947,17 +6049,28 @@ public class ConnectivityServiceTest {
                && caps.hasTransport(TRANSPORT_VPN)
                && caps.hasTransport(TRANSPORT_WIFI));

        // Change the VPN's capabilities somehow (specifically, disconnect wifi).
        mWiFiNetworkAgent.disconnect();
        callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
        callback.expectCapabilitiesThat(mMockVpn, (caps)
                -> caps.getUids().size() == 2
                && caps.getUids().contains(new UidRange(uid, uid))
                && caps.getUids().contains(UidRange.createForUser(restrictedUserId))
                && 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);
        handler.post(() -> mServiceContext.sendBroadcast(removedIntent));

        // Expect that the VPN gains the UID range for the restricted user.
        // Expect that the VPN gains the UID range for the restricted user, and that the capability
        // change made just before that (i.e., loss of TRANSPORT_WIFI) is preserved.
        callback.expectCapabilitiesThat(mMockVpn, (caps)
                -> caps.getUids().size() == 1
                && caps.getUids().contains(new UidRange(uid, uid))
                && caps.hasTransport(TRANSPORT_VPN)
                && caps.hasTransport(TRANSPORT_WIFI));
                && !caps.hasTransport(TRANSPORT_WIFI));
    }

    @Test
+0 −105
Original line number Diff line number Diff line
@@ -21,15 +21,6 @@ import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
import static android.content.pm.UserInfo.FLAG_PRIMARY;
import static android.content.pm.UserInfo.FLAG_RESTRICTED;
import static android.net.ConnectivityManager.NetworkCallback;
import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -621,102 +612,6 @@ public class VpnTest {
        order.verify(mNotificationManager).cancel(anyString(), anyInt());
    }

    @Test
    public void testCapabilities() {
        setMockedUsers(primaryUser);

        final Network mobile = new Network(1);
        final Network wifi = new Network(2);

        final Map<Network, NetworkCapabilities> networks = new HashMap<>();
        networks.put(
                mobile,
                new NetworkCapabilities()
                        .addTransportType(TRANSPORT_CELLULAR)
                        .addCapability(NET_CAPABILITY_INTERNET)
                        .addCapability(NET_CAPABILITY_NOT_CONGESTED)
                        .setLinkDownstreamBandwidthKbps(10));
        networks.put(
                wifi,
                new NetworkCapabilities()
                        .addTransportType(TRANSPORT_WIFI)
                        .addCapability(NET_CAPABILITY_INTERNET)
                        .addCapability(NET_CAPABILITY_NOT_METERED)
                        .addCapability(NET_CAPABILITY_NOT_ROAMING)
                        .addCapability(NET_CAPABILITY_NOT_CONGESTED)
                        .addCapability(NET_CAPABILITY_NOT_SUSPENDED)
                        .setLinkUpstreamBandwidthKbps(20));
        setMockedNetworks(networks);

        final NetworkCapabilities caps = new NetworkCapabilities();

        Vpn.applyUnderlyingCapabilities(
                mConnectivityManager, new Network[] {}, caps, false /* isAlwaysMetered */);
        assertTrue(caps.hasTransport(TRANSPORT_VPN));
        assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
        assertFalse(caps.hasTransport(TRANSPORT_WIFI));
        assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkDownstreamBandwidthKbps());
        assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkUpstreamBandwidthKbps());
        assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));

        Vpn.applyUnderlyingCapabilities(
                mConnectivityManager,
                new Network[] {mobile},
                caps,
                false /* isAlwaysMetered */);
        assertTrue(caps.hasTransport(TRANSPORT_VPN));
        assertTrue(caps.hasTransport(TRANSPORT_CELLULAR));
        assertFalse(caps.hasTransport(TRANSPORT_WIFI));
        assertEquals(10, caps.getLinkDownstreamBandwidthKbps());
        assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkUpstreamBandwidthKbps());
        assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
        assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
        assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));

        Vpn.applyUnderlyingCapabilities(
                mConnectivityManager, new Network[] {wifi}, caps, false /* isAlwaysMetered */);
        assertTrue(caps.hasTransport(TRANSPORT_VPN));
        assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
        assertTrue(caps.hasTransport(TRANSPORT_WIFI));
        assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkDownstreamBandwidthKbps());
        assertEquals(20, caps.getLinkUpstreamBandwidthKbps());
        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));

        Vpn.applyUnderlyingCapabilities(
                mConnectivityManager, new Network[] {wifi}, caps, true /* isAlwaysMetered */);
        assertTrue(caps.hasTransport(TRANSPORT_VPN));
        assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
        assertTrue(caps.hasTransport(TRANSPORT_WIFI));
        assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkDownstreamBandwidthKbps());
        assertEquals(20, caps.getLinkUpstreamBandwidthKbps());
        assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));

        Vpn.applyUnderlyingCapabilities(
                mConnectivityManager,
                new Network[] {mobile, wifi},
                caps,
                false /* isAlwaysMetered */);
        assertTrue(caps.hasTransport(TRANSPORT_VPN));
        assertTrue(caps.hasTransport(TRANSPORT_CELLULAR));
        assertTrue(caps.hasTransport(TRANSPORT_WIFI));
        assertEquals(10, caps.getLinkDownstreamBandwidthKbps());
        assertEquals(20, caps.getLinkUpstreamBandwidthKbps());
        assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
        assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
    }

    /**
     * The profile name should NOT change between releases for backwards compatibility
     *