diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 21dd320350..761c033660 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -17,6 +17,7 @@ package android.net; import android.annotation.IntDef; +import android.net.ConnectivityManager.NetworkCallback; import android.os.Parcel; import android.os.Parcelable; @@ -30,15 +31,24 @@ import java.util.Objects; import java.util.StringJoiner; /** - * This class represents the capabilities of a network. This is used both to specify - * needs to {@link ConnectivityManager} and when inspecting a network. - * - * Note that this replaces the old {@link ConnectivityManager#TYPE_MOBILE} method - * of network selection. Rather than indicate a need for Wi-Fi because an application - * needs high bandwidth and risk obsolescence when a new, fast network appears (like LTE), - * the application should specify it needs high bandwidth. Similarly if an application - * needs an unmetered network for a bulk transfer it can specify that rather than assuming - * all cellular based connections are metered and all Wi-Fi based connections are not. + * Representation of the capabilities of a network. This object serves two + * purposes: + * + *

+ * This replaces the old {@link ConnectivityManager#TYPE_MOBILE} method of + * network selection. Rather than indicate a need for Wi-Fi because an + * application needs high bandwidth and risk obsolescence when a new, fast + * network appears (like LTE), the application should specify it needs high + * bandwidth. Similarly if an application needs an unmetered network for a bulk + * transfer it can specify that rather than assuming all cellular based + * connections are metered and all Wi-Fi based connections are not. */ public final class NetworkCapabilities implements Parcelable { private static final String TAG = "NetworkCapabilities"; @@ -101,6 +111,7 @@ public final class NetworkCapabilities implements Parcelable { NET_CAPABILITY_NOT_VPN, NET_CAPABILITY_VALIDATED, NET_CAPABILITY_CAPTIVE_PORTAL, + NET_CAPABILITY_NOT_ROAMING, NET_CAPABILITY_FOREGROUND, }) public @interface NetCapability { } @@ -217,12 +228,17 @@ public final class NetworkCapabilities implements Parcelable { */ public static final int NET_CAPABILITY_CAPTIVE_PORTAL = 17; + /** + * Indicates that this network is not roaming. + */ + public static final int NET_CAPABILITY_NOT_ROAMING = 18; + /** * Indicates that this network is available for use by apps, and not a network that is being * kept up in the background to facilitate fast network switching. * @hide */ - public static final int NET_CAPABILITY_FOREGROUND = 18; + public static final int NET_CAPABILITY_FOREGROUND = 19; private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS; private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_FOREGROUND; @@ -237,6 +253,7 @@ public final class NetworkCapabilities implements Parcelable { (1 << NET_CAPABILITY_TRUSTED) | (1 << NET_CAPABILITY_VALIDATED) | (1 << NET_CAPABILITY_CAPTIVE_PORTAL) | + (1 << NET_CAPABILITY_NOT_ROAMING) | (1 << NET_CAPABILITY_FOREGROUND); /** @@ -315,6 +332,21 @@ public final class NetworkCapabilities implements Parcelable { return this; } + /** + * Sets (or clears) the given capability on this {@link NetworkCapabilities} + * instance. + * + * @hide + */ + public NetworkCapabilities setCapability(@NetCapability int capability, boolean value) { + if (value) { + addCapability(capability); + } else { + removeCapability(capability); + } + return this; + } + /** * Gets all the capabilities set on this {@code NetworkCapability} instance. * @@ -325,6 +357,15 @@ public final class NetworkCapabilities implements Parcelable { return BitUtils.unpackBits(mNetworkCapabilities); } + /** + * Sets all the capabilities set on this {@code NetworkCapability} instance. + * + * @hide + */ + public void setCapabilities(@NetCapability int[] capabilities) { + mNetworkCapabilities = BitUtils.packBits(capabilities); + } + /** * Tests for the presence of a capabilitity on this instance. * @@ -514,6 +555,21 @@ public final class NetworkCapabilities implements Parcelable { return this; } + /** + * Sets (or clears) the given transport on this {@link NetworkCapabilities} + * instance. + * + * @hide + */ + public NetworkCapabilities setTransportType(@Transport int transportType, boolean value) { + if (value) { + addTransportType(transportType); + } else { + removeTransportType(transportType); + } + return this; + } + /** * Gets all the transports set on this {@code NetworkCapability} instance. * @@ -524,6 +580,15 @@ public final class NetworkCapabilities implements Parcelable { return BitUtils.unpackBits(mTransportTypes); } + /** + * Sets all the transports set on this {@code NetworkCapability} instance. + * + * @hide + */ + public void setTransportTypes(@Transport int[] transportTypes) { + mTransportTypes = BitUtils.packBits(transportTypes); + } + /** * Tests for the presence of a transport on this instance. * @@ -548,13 +613,19 @@ public final class NetworkCapabilities implements Parcelable { return (nc.mTransportTypes == this.mTransportTypes); } + /** + * Value indicating that link bandwidth is unspecified. + * @hide + */ + public static final int LINK_BANDWIDTH_UNSPECIFIED = 0; + /** * Passive link bandwidth. This is a rough guide of the expected peak bandwidth * for the first hop on the given transport. It is not measured, but may take into account * link parameters (Radio technology, allocated channels, etc). */ - private int mLinkUpBandwidthKbps; - private int mLinkDownBandwidthKbps; + private int mLinkUpBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED; + private int mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED; /** * Sets the upstream bandwidth for this network in Kbps. This always only refers to @@ -571,8 +642,9 @@ public final class NetworkCapabilities implements Parcelable { * @param upKbps the estimated first hop upstream (device to network) bandwidth. * @hide */ - public void setLinkUpstreamBandwidthKbps(int upKbps) { + public NetworkCapabilities setLinkUpstreamBandwidthKbps(int upKbps) { mLinkUpBandwidthKbps = upKbps; + return this; } /** @@ -600,8 +672,9 @@ public final class NetworkCapabilities implements Parcelable { * @param downKbps the estimated first hop downstream (network to device) bandwidth. * @hide */ - public void setLinkDownstreamBandwidthKbps(int downKbps) { + public NetworkCapabilities setLinkDownstreamBandwidthKbps(int downKbps) { mLinkDownBandwidthKbps = downKbps; + return this; } /** @@ -628,6 +701,20 @@ public final class NetworkCapabilities implements Parcelable { return (this.mLinkUpBandwidthKbps == nc.mLinkUpBandwidthKbps && this.mLinkDownBandwidthKbps == nc.mLinkDownBandwidthKbps); } + /** @hide */ + public static int minBandwidth(int a, int b) { + if (a == LINK_BANDWIDTH_UNSPECIFIED) { + return b; + } else if (b == LINK_BANDWIDTH_UNSPECIFIED) { + return a; + } else { + return Math.min(a, b); + } + } + /** @hide */ + public static int maxBandwidth(int a, int b) { + return Math.max(a, b); + } private NetworkSpecifier mNetworkSpecifier = null; @@ -708,8 +795,9 @@ public final class NetworkCapabilities implements Parcelable { * @param signalStrength the bearer-specific signal strength. * @hide */ - public void setSignalStrength(int signalStrength) { + public NetworkCapabilities setSignalStrength(int signalStrength) { mSignalStrength = signalStrength; + return this; } /** @@ -970,6 +1058,7 @@ public final class NetworkCapabilities implements Parcelable { case NET_CAPABILITY_NOT_VPN: return "NOT_VPN"; case NET_CAPABILITY_VALIDATED: return "VALIDATED"; case NET_CAPABILITY_CAPTIVE_PORTAL: return "CAPTIVE_PORTAL"; + case NET_CAPABILITY_NOT_ROAMING: return "NOT_ROAMING"; case NET_CAPABILITY_FOREGROUND: return "FOREGROUND"; default: return Integer.toString(capability); } diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java index 818aa21162..e6ad89a20b 100644 --- a/core/java/android/net/NetworkInfo.java +++ b/core/java/android/net/NetworkInfo.java @@ -305,11 +305,17 @@ public class NetworkInfo implements Parcelable { } /** - * Indicates whether the device is currently roaming on this network. - * When {@code true}, it suggests that use of data on this network - * may incur extra costs. + * Indicates whether the device is currently roaming on this network. When + * {@code true}, it suggests that use of data on this network may incur + * extra costs. + * * @return {@code true} if roaming is in effect, {@code false} otherwise. + * @deprecated Callers should switch to checking + * {@link NetworkCapabilities#NET_CAPABILITY_NOT_ROAMING} + * instead, since that handles more complex situations, such as + * VPNs. */ + @Deprecated public boolean isRoaming() { synchronized (this) { return mIsRoaming; @@ -318,6 +324,7 @@ public class NetworkInfo implements Parcelable { /** {@hide} */ @VisibleForTesting + @Deprecated public void setRoaming(boolean isRoaming) { synchronized (this) { mIsRoaming = isRoaming; diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index d819a33d92..f6ca87fdb9 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -29,7 +29,10 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; 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_VALIDATED; +import static android.net.NetworkCapabilities.TRANSPORT_VPN; + import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.Nullable; @@ -70,6 +73,7 @@ import android.net.ProxyInfo; import android.net.RouteInfo; import android.net.UidRange; import android.net.Uri; +import android.net.VpnService; import android.net.metrics.IpConnectivityLog; import android.net.metrics.NetworkEvent; import android.net.util.MultinetworkPolicyTracker; @@ -4647,10 +4651,12 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - final NetworkCapabilities prevNc = nai.networkCapabilities; + final NetworkCapabilities prevNc; synchronized (nai) { + prevNc = nai.networkCapabilities; nai.networkCapabilities = networkCapabilities; } + if (nai.getCurrentScore() == oldScore && networkCapabilities.equalRequestableCapabilities(prevNc)) { // If the requestable capabilities haven't changed, and the score hasn't changed, then @@ -4664,6 +4670,28 @@ public class ConnectivityService extends IConnectivityManager.Stub rematchAllNetworksAndRequests(nai, oldScore); notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED); } + + // Report changes that are interesting for network statistics tracking. + if (prevNc != null) { + final boolean meteredChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_METERED) != + networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED); + final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING) != + networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING); + if (meteredChanged || roamingChanged) { + notifyIfacesChangedForNetworkStats(); + } + } + + if (!networkCapabilities.hasTransport(TRANSPORT_VPN)) { + // Tell VPNs about updated capabilities, since they may need to + // bubble those changes through. + synchronized (mVpns) { + for (int i = 0; i < mVpns.size(); i++) { + final Vpn vpn = mVpns.valueAt(i); + vpn.updateCapabilities(); + } + } + } } public void handleUpdateLinkProperties(NetworkAgentInfo nai, LinkProperties newLp) { @@ -5197,14 +5225,6 @@ public class ConnectivityService extends IConnectivityManager.Stub } notifyLockdownVpn(networkAgent); - if (oldInfo != null && oldInfo.getState() == state) { - if (oldInfo.isRoaming() != newInfo.isRoaming()) { - if (VDBG) log("roaming status changed, notifying NetworkStatsService"); - notifyIfacesChangedForNetworkStats(); - } else if (VDBG) log("ignoring duplicate network state non-change"); - // In either case, no further work should be needed. - return; - } if (DBG) { log(networkAgent.name() + " EVENT_NETWORK_INFO_CHANGED, going from " + (oldInfo == null ? "null" : oldInfo.getState()) + diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java index 743344f589..e6170cb42c 100644 --- a/tests/net/java/android/net/NetworkCapabilitiesTest.java +++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java @@ -16,6 +16,7 @@ package android.net; +import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS; import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS; @@ -27,13 +28,12 @@ import static android.net.NetworkCapabilities.RESTRICTED_CAPABILITIES; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; - -import android.net.NetworkCapabilities; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; @@ -168,4 +168,25 @@ public class NetworkCapabilitiesTest { assertNotEquals("", nc1.describeImmutableDifferences(nc2)); assertEquals("", nc1.describeImmutableDifferences(nc1)); } + + @Test + public void testLinkBandwidthUtils() { + assertEquals(LINK_BANDWIDTH_UNSPECIFIED, NetworkCapabilities + .minBandwidth(LINK_BANDWIDTH_UNSPECIFIED, LINK_BANDWIDTH_UNSPECIFIED)); + assertEquals(10, NetworkCapabilities + .minBandwidth(LINK_BANDWIDTH_UNSPECIFIED, 10)); + assertEquals(10, NetworkCapabilities + .minBandwidth(10, LINK_BANDWIDTH_UNSPECIFIED)); + assertEquals(10, NetworkCapabilities + .minBandwidth(10, 20)); + + assertEquals(LINK_BANDWIDTH_UNSPECIFIED, NetworkCapabilities + .maxBandwidth(LINK_BANDWIDTH_UNSPECIFIED, LINK_BANDWIDTH_UNSPECIFIED)); + assertEquals(10, NetworkCapabilities + .maxBandwidth(LINK_BANDWIDTH_UNSPECIFIED, 10)); + assertEquals(10, NetworkCapabilities + .maxBandwidth(10, LINK_BANDWIDTH_UNSPECIFIED)); + assertEquals(20, NetworkCapabilities + .maxBandwidth(10, 20)); + } } diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 27a29b68e1..113cd37f1a 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -23,21 +23,44 @@ import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA; import static android.net.ConnectivityManager.TYPE_MOBILE_MMS; import static android.net.ConnectivityManager.TYPE_NONE; import static android.net.ConnectivityManager.TYPE_WIFI; -import static android.net.ConnectivityManager.getNetworkTypeName; -import static android.net.NetworkCapabilities.*; +import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; +import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND; +import static android.net.NetworkCapabilities.NET_CAPABILITY_FOTA; +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_METERED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_SUPL; +import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P; +import static android.net.NetworkCapabilities.NET_CAPABILITY_XCAP; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE; + +import static com.android.internal.util.TestUtils.waitForIdleHandler; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; 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 com.android.internal.util.TestUtils.waitForIdleHandler; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.NotificationManager; @@ -45,7 +68,6 @@ import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; -import android.content.ContextWrapper; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; @@ -64,7 +86,6 @@ import android.net.MatchAllNetworkSpecifier; import android.net.Network; import android.net.NetworkAgent; import android.net.NetworkCapabilities; -import android.net.NetworkConfig; import android.net.NetworkFactory; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; @@ -79,13 +100,9 @@ import android.net.util.MultinetworkPolicyTracker; import android.os.ConditionVariable; import android.os.Handler; import android.os.HandlerThread; -import android.os.IBinder; import android.os.INetworkManagementService; import android.os.Looper; import android.os.Message; -import android.os.MessageQueue; -import android.os.Messenger; -import android.os.MessageQueue.IdleHandler; import android.os.Parcel; import android.os.Parcelable; import android.os.Process; @@ -96,10 +113,8 @@ import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.test.mock.MockContentResolver; -import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; -import android.util.LogPrinter; import com.android.internal.util.WakeupMessage; import com.android.internal.util.test.BroadcastInterceptingContext; @@ -109,7 +124,6 @@ import com.android.server.connectivity.IpConnectivityMetrics; import com.android.server.connectivity.MockableSystemProperties; import com.android.server.connectivity.NetworkAgentInfo; import com.android.server.connectivity.NetworkMonitor; -import com.android.server.connectivity.NetworkMonitor.CaptivePortalProbeResult; import com.android.server.net.NetworkPinner; import com.android.server.net.NetworkPolicyManagerInternal; @@ -160,6 +174,7 @@ public class ConnectivityServiceTest { @Mock IpConnectivityMetrics.Logger mMetricsService; @Mock DefaultNetworkMetrics mDefaultNetworkMetrics; + @Mock INetworkStatsService mStatsService; // This class exists to test bindProcessToNetwork and getBoundNetworkForProcess. These methods // do not go through ConnectivityService but talk to netd directly, so they don't automatically @@ -858,7 +873,7 @@ public class ConnectivityServiceTest { NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class)); mService = new WrappedConnectivityService(mServiceContext, mock(INetworkManagementService.class), - mock(INetworkStatsService.class), + mStatsService, mock(INetworkPolicyManager.class), mock(IpConnectivityLog.class)); @@ -3440,6 +3455,40 @@ public class ConnectivityServiceTest { mCm.unregisterNetworkCallback(networkCallback); } + @Test + public void testStatsIfacesChanged() throws Exception { + mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + + // Simple connection should have updated ifaces + mCellNetworkAgent.connect(false); + waitForIdle(); + verify(mStatsService, atLeastOnce()).forceUpdateIfaces(); + reset(mStatsService); + + // Metered change should update ifaces + mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); + waitForIdle(); + verify(mStatsService, atLeastOnce()).forceUpdateIfaces(); + reset(mStatsService); + + mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); + waitForIdle(); + verify(mStatsService, atLeastOnce()).forceUpdateIfaces(); + reset(mStatsService); + + // Captive portal change shouldn't update ifaces + mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL); + waitForIdle(); + verify(mStatsService, never()).forceUpdateIfaces(); + reset(mStatsService); + + // Roaming change should update ifaces + mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING); + waitForIdle(); + verify(mStatsService, atLeastOnce()).forceUpdateIfaces(); + reset(mStatsService); + } + private void checkDirectlyConnectedRoutes(Object callbackObj, Collection linkAddresses, Collection otherRoutes) { assertTrue(callbackObj instanceof LinkProperties); diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index fe396c3a5c..c29363cd1a 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -20,11 +20,30 @@ import static android.content.pm.UserInfo.FLAG_ADMIN; 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.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; +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.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.AdditionalMatchers.*; -import static org.mockito.Mockito.*; +import static org.mockito.AdditionalMatchers.aryEq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.annotation.UserIdInt; import android.app.AppOpsManager; @@ -36,6 +55,9 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; import android.content.res.Resources; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; import android.net.NetworkInfo.DetailedState; import android.net.UidRange; import android.net.VpnService; @@ -45,17 +67,17 @@ import android.os.INetworkManagementService; import android.os.Looper; import android.os.UserHandle; import android.os.UserManager; -import android.support.test.runner.AndroidJUnit4; import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; import android.util.ArrayMap; import android.util.ArraySet; import com.android.internal.R; import com.android.internal.net.VpnConfig; -import org.junit.runner.RunWith; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.InOrder; import org.mockito.Mock; @@ -64,6 +86,7 @@ import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -114,6 +137,7 @@ public class VpnTest { @Mock private AppOpsManager mAppOps; @Mock private NotificationManager mNotificationManager; @Mock private Vpn.SystemServices mSystemServices; + @Mock private ConnectivityManager mConnectivityManager; @Before public void setUp() throws Exception { @@ -127,6 +151,8 @@ public class VpnTest { when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps); when(mContext.getSystemService(eq(Context.NOTIFICATION_SERVICE))) .thenReturn(mNotificationManager); + when(mContext.getSystemService(eq(Context.CONNECTIVITY_SERVICE))) + .thenReturn(mConnectivityManager); when(mContext.getString(R.string.config_customVpnAlwaysOnDisconnectedDialogComponent)) .thenReturn(Resources.getSystem().getString( R.string.config_customVpnAlwaysOnDisconnectedDialogComponent)); @@ -397,6 +423,66 @@ public class VpnTest { order.verify(mNotificationManager).cancelAsUser(anyString(), anyInt(), eq(userHandle)); } + @Test + public void testCapabilities() { + final Vpn vpn = createVpn(primaryUser.id); + setMockedUsers(primaryUser); + + final Network mobile = new Network(1); + final Network wifi = new Network(2); + + final Map networks = new HashMap<>(); + networks.put(mobile, new NetworkCapabilities() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_METERED) + .setLinkDownstreamBandwidthKbps(10)); + networks.put(wifi, new NetworkCapabilities() + .addTransportType(TRANSPORT_WIFI) + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_ROAMING) + .setLinkUpstreamBandwidthKbps(20)); + setMockedNetworks(networks); + + final NetworkCapabilities caps = new NetworkCapabilities(); + + Vpn.updateCapabilities(mConnectivityManager, new Network[] { }, caps); + 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)); + + Vpn.updateCapabilities(mConnectivityManager, new Network[] { mobile }, caps); + 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()); + assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_METERED)); + assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)); + + Vpn.updateCapabilities(mConnectivityManager, new Network[] { wifi }, caps); + 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)); + + Vpn.updateCapabilities(mConnectivityManager, new Network[] { mobile, wifi }, caps); + 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)); + } + /** * Mock some methods of vpn object. */ @@ -463,4 +549,11 @@ public class VpnTest { } catch (Exception e) { } } + + private void setMockedNetworks(final Map networks) { + doAnswer(invocation -> { + final Network network = (Network) invocation.getArguments()[0]; + return networks.get(network); + }).when(mConnectivityManager).getNetworkCapabilities(any()); + } } diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java index 814a626633..ecc99323d7 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java @@ -1159,9 +1159,8 @@ public class NetworkStatsServiceTest { final LinkProperties prop = new LinkProperties(); prop.setInterfaceName(TEST_IFACE); final NetworkCapabilities capabilities = new NetworkCapabilities(); - if (!isMetered) { - capabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); - } + capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, !isMetered); + capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true); return new NetworkState(info, prop, capabilities, null, null, TEST_SSID); } @@ -1177,6 +1176,8 @@ public class NetworkStatsServiceTest { final LinkProperties prop = new LinkProperties(); prop.setInterfaceName(TEST_IFACE); final NetworkCapabilities capabilities = new NetworkCapabilities(); + capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false); + capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming); return new NetworkState(info, prop, capabilities, null, subscriberId, null); } @@ -1186,6 +1187,8 @@ public class NetworkStatsServiceTest { final LinkProperties prop = new LinkProperties(); prop.setInterfaceName(iface); final NetworkCapabilities capabilities = new NetworkCapabilities(); + capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false); + capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true); return new NetworkState(info, prop, capabilities, null, null, null); }