diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java index 74d50f8fd06298136f4707a8f45b27f80015d55d..f959114451432f17c26283d38a7f6ec9f41655c0 100644 --- a/framework/src/android/net/NetworkCapabilities.java +++ b/framework/src/android/net/NetworkCapabilities.java @@ -453,6 +453,7 @@ public final class NetworkCapabilities implements Parcelable { NET_CAPABILITY_MMTEL, NET_CAPABILITY_PRIORITIZE_LATENCY, NET_CAPABILITY_PRIORITIZE_BANDWIDTH, + NET_CAPABILITY_LOCAL_NETWORK, }) public @interface NetCapability { } @@ -714,7 +715,21 @@ public final class NetworkCapabilities implements Parcelable { */ public static final int NET_CAPABILITY_PRIORITIZE_BANDWIDTH = 35; - private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_PRIORITIZE_BANDWIDTH; + /** + * This is a local network, e.g. a tethering downstream or a P2P direct network. + * + * <p> + * Note that local networks are not sent to callbacks by default. To receive callbacks about + * them, the {@link NetworkRequest} instance must be prepared to see them, either by + * adding the capability with {@link NetworkRequest.Builder#addCapability}, by removing + * this forbidden capability with {@link NetworkRequest.Builder#removeForbiddenCapability}, + * or by clearing all capabilites with {@link NetworkRequest.Builder#clearCapabilities()}. + * </p> + * @hide + */ + public static final int NET_CAPABILITY_LOCAL_NETWORK = 36; + + private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_LOCAL_NETWORK; // Set all bits up to the MAX_NET_CAPABILITY-th bit private static final long ALL_VALID_CAPABILITIES = (2L << MAX_NET_CAPABILITY) - 1; @@ -859,7 +874,7 @@ public final class NetworkCapabilities implements Parcelable { } /** - * Removes (if found) the given forbidden capability from this {@code NetworkCapability} + * Removes (if found) the given forbidden capability from this {@link NetworkCapabilities} * instance that were added via addForbiddenCapability(int) or setCapabilities(int[], int[]). * * @param capability the capability to be removed. @@ -872,6 +887,16 @@ public final class NetworkCapabilities implements Parcelable { return this; } + /** + * Removes all forbidden capabilities from this {@link NetworkCapabilities} instance. + * @return This NetworkCapabilities instance, to facilitate chaining. + * @hide + */ + public @NonNull NetworkCapabilities removeAllForbiddenCapabilities() { + mForbiddenNetworkCapabilities = 0; + return this; + } + /** * Sets (or clears) the given capability on this {@link NetworkCapabilities} * instance. @@ -1039,11 +1064,12 @@ public final class NetworkCapabilities implements Parcelable { } /** - * Check if this NetworkCapabilities has system managed capabilities or not. + * Check if this NetworkCapabilities has connectivity-managed capabilities or not. * @hide */ public boolean hasConnectivityManagedCapability() { - return ((mNetworkCapabilities & CONNECTIVITY_MANAGED_CAPABILITIES) != 0); + return (mNetworkCapabilities & CONNECTIVITY_MANAGED_CAPABILITIES) != 0 + || mForbiddenNetworkCapabilities != 0; } /** @@ -2523,6 +2549,7 @@ public final class NetworkCapabilities implements Parcelable { case NET_CAPABILITY_MMTEL: return "MMTEL"; case NET_CAPABILITY_PRIORITIZE_LATENCY: return "PRIORITIZE_LATENCY"; case NET_CAPABILITY_PRIORITIZE_BANDWIDTH: return "PRIORITIZE_BANDWIDTH"; + case NET_CAPABILITY_LOCAL_NETWORK: return "LOCAL_NETWORK"; default: return Integer.toString(capability); } } diff --git a/framework/src/android/net/NetworkRequest.java b/framework/src/android/net/NetworkRequest.java index 5cc9f1b605dac9d6e7edffc10d6cb116b5810c8a..9824faa1b514d813e1ddca46053b46173aaab966 100644 --- a/framework/src/android/net/NetworkRequest.java +++ b/framework/src/android/net/NetworkRequest.java @@ -20,6 +20,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +import static android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK; 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; @@ -283,6 +284,15 @@ public class NetworkRequest implements Parcelable { NET_CAPABILITY_TRUSTED, NET_CAPABILITY_VALIDATED); + /** + * Capabilities that are forbidden by default. + * Forbidden capabilities only make sense in NetworkRequest, not for network agents. + * Therefore these capabilities are only in NetworkRequest. + */ + private static final int[] DEFAULT_FORBIDDEN_CAPABILITIES = new int[] { + NET_CAPABILITY_LOCAL_NETWORK + }; + private final NetworkCapabilities mNetworkCapabilities; // A boolean that represents whether the NOT_VCN_MANAGED capability should be deduced when @@ -298,6 +308,16 @@ public class NetworkRequest implements Parcelable { // it for apps that do not have the NETWORK_SETTINGS permission. mNetworkCapabilities = new NetworkCapabilities(); mNetworkCapabilities.setSingleUid(Process.myUid()); + // Default forbidden capabilities are foremost meant to help with backward + // compatibility. When adding new types of network identified by a capability that + // might confuse older apps, a default forbidden capability will have apps not see + // these networks unless they explicitly ask for it. + // If the app called clearCapabilities() it will see everything, but then it + // can be argued that it's fair to send them too, since it asked for everything + // explicitly. + for (final int forbiddenCap : DEFAULT_FORBIDDEN_CAPABILITIES) { + mNetworkCapabilities.addForbiddenCapability(forbiddenCap); + } } /** diff --git a/framework/src/android/net/NetworkScore.java b/framework/src/android/net/NetworkScore.java index 2522958310dc888456eb51044c63c1bbd226819d..00382f602b1f84ffa27eab729727e7cb83e89acd 100644 --- a/framework/src/android/net/NetworkScore.java +++ b/framework/src/android/net/NetworkScore.java @@ -45,7 +45,8 @@ public final class NetworkScore implements Parcelable { @IntDef(value = { KEEP_CONNECTED_NONE, KEEP_CONNECTED_FOR_HANDOVER, - KEEP_CONNECTED_FOR_TEST + KEEP_CONNECTED_FOR_TEST, + KEEP_CONNECTED_DOWNSTREAM_NETWORK }) public @interface KeepConnectedReason { } @@ -64,6 +65,12 @@ public final class NetworkScore implements Parcelable { * @hide */ public static final int KEEP_CONNECTED_FOR_TEST = 2; + /** + * Keep this network connected even if there is no outstanding request for it, because + * it is a downstream network. + * @hide + */ + public static final int KEEP_CONNECTED_DOWNSTREAM_NETWORK = 3; // Agent-managed policies // This network should lose to a wifi that has ever been validated diff --git a/service/src/com/android/server/connectivity/NetworkAgentInfo.java b/service/src/com/android/server/connectivity/NetworkAgentInfo.java index bdd841f9ed6ba0fe36589c349ce04b141a2dcb42..0f72cd462d54b387d5b6df1c08710587e166ca5b 100644 --- a/service/src/com/android/server/connectivity/NetworkAgentInfo.java +++ b/service/src/com/android/server/connectivity/NetworkAgentInfo.java @@ -453,6 +453,8 @@ public class NetworkAgentInfo implements NetworkRanker.Scoreable { * apply to the allowedUids field. * They also should not mutate immutable capabilities, although for backward-compatibility * this is not enforced and limited to just a log. + * Forbidden capabilities also make no sense for networks, so they are disallowed and + * will be ignored with a warning. * * @param carrierPrivilegeAuthenticator the authenticator, to check access UIDs. */ @@ -461,6 +463,7 @@ public class NetworkAgentInfo implements NetworkRanker.Scoreable { final NetworkCapabilities nc = new NetworkCapabilities(mDeclaredCapabilitiesUnsanitized); if (nc.hasConnectivityManagedCapability()) { Log.wtf(TAG, "BUG: " + this + " has CS-managed capability."); + nc.removeAllForbiddenCapabilities(); } if (networkCapabilities.getOwnerUid() != nc.getOwnerUid()) { Log.e(TAG, toShortString() + ": ignoring attempt to change owner from " diff --git a/staticlibs/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt b/staticlibs/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt index 35f22b9e587d45771b05382d7a3ceeeb2050d7e6..46229b0cd2de58b003f440b835cdfaa6a7ab6de6 100644 --- a/staticlibs/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt +++ b/staticlibs/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt @@ -27,6 +27,9 @@ import org.junit.runners.model.Statement @Deprecated("Use Build.VERSION_CODES", ReplaceWith("Build.VERSION_CODES.S_V2")) const val SC_V2 = Build.VERSION_CODES.S_V2 +// TODO: Remove this when Build.VERSION_CODES.VANILLA_ICE_CREAM is available in all branches +// where this code builds +const val VANILLA_ICE_CREAM = 35 // Bui1ld.VERSION_CODES.VANILLA_ICE_CREAM private val MAX_TARGET_SDK_ANNOTATION_RE = Pattern.compile("MaxTargetSdk([0-9]+)$") private val targetSdk = InstrumentationRegistry.getContext().applicationInfo.targetSdkVersion diff --git a/tests/common/java/android/net/NetworkCapabilitiesTest.java b/tests/common/java/android/net/NetworkCapabilitiesTest.java index aae342574f81061e033dc309eed93f4b8fe00eb4..bec9a4a61646aa848b28762ad06b1449e674001a 100644 --- a/tests/common/java/android/net/NetworkCapabilitiesTest.java +++ b/tests/common/java/android/net/NetworkCapabilitiesTest.java @@ -26,6 +26,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS; import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE; import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +import static android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK; 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; @@ -63,6 +64,7 @@ import static android.os.Process.INVALID_UID; import static com.android.modules.utils.build.SdkLevel.isAtLeastR; import static com.android.modules.utils.build.SdkLevel.isAtLeastS; import static com.android.modules.utils.build.SdkLevel.isAtLeastT; +import static com.android.modules.utils.build.SdkLevel.isAtLeastV; import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2; import static com.android.testutils.MiscAsserts.assertEmpty; import static com.android.testutils.MiscAsserts.assertThrows; @@ -369,6 +371,9 @@ public class NetworkCapabilitiesTest { .addCapability(NET_CAPABILITY_INTERNET) .addCapability(NET_CAPABILITY_EIMS) .addCapability(NET_CAPABILITY_NOT_METERED); + if (isAtLeastV()) { + netCap.addCapability(NET_CAPABILITY_LOCAL_NETWORK); + } if (isAtLeastS()) { final ArraySet<Integer> allowedUids = new ArraySet<>(); allowedUids.add(4); diff --git a/tests/cts/net/src/android/net/cts/NetworkRequestTest.java b/tests/cts/net/src/android/net/cts/NetworkRequestTest.java index 718ca6dea48f8e82fb01337cd46075e8769bf58d..594f3fbaab8f2071a8d68bf350eaa1114aab0a21 100644 --- a/tests/cts/net/src/android/net/cts/NetworkRequestTest.java +++ b/tests/cts/net/src/android/net/cts/NetworkRequestTest.java @@ -19,8 +19,10 @@ package android.net.cts; import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; import static android.net.NetworkCapabilities.NET_CAPABILITY_FOTA; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +import static android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK; import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.NetworkCapabilities.NET_CAPABILITY_SUPL; import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED; import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH; @@ -28,6 +30,8 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static com.android.testutils.DevSdkIgnoreRuleKt.VANILLA_ICE_CREAM; + import static junit.framework.Assert.fail; import static org.junit.Assert.assertArrayEquals; @@ -489,6 +493,32 @@ public class NetworkRequestTest { assertArrayEquals(netCapabilities, nr.getCapabilities()); } + @Test @IgnoreUpTo(VANILLA_ICE_CREAM) + public void testDefaultCapabilities() { + final NetworkRequest defaultNR = new NetworkRequest.Builder().build(); + assertTrue(defaultNR.hasForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)); + assertFalse(defaultNR.hasCapability(NET_CAPABILITY_LOCAL_NETWORK)); + assertTrue(defaultNR.hasCapability(NET_CAPABILITY_NOT_VPN)); + + final NetworkCapabilities emptyNC = + NetworkCapabilities.Builder.withoutDefaultCapabilities().build(); + assertFalse(defaultNR.canBeSatisfiedBy(emptyNC)); + + // defaultNC represent the capabilities of a network agent, so they must not contain + // forbidden capabilities by default. + final NetworkCapabilities defaultNC = new NetworkCapabilities.Builder().build(); + assertArrayEquals(new int[0], defaultNC.getForbiddenCapabilities()); + // A default NR can be satisfied by default NC. + assertTrue(defaultNR.canBeSatisfiedBy(defaultNC)); + + // Conversely, network requests have forbidden capabilities by default to manage + // backward compatibility, so test that these forbidden capabilities are in place. + // Starting in V, NET_CAPABILITY_LOCAL_NETWORK is introduced but is not seen by + // default, thanks to a default forbidden capability in NetworkRequest. + defaultNC.addCapability(NET_CAPABILITY_LOCAL_NETWORK); + assertFalse(defaultNR.canBeSatisfiedBy(defaultNC)); + } + @Test public void testBuildRequestFromExistingRequestWithBuilder() { assumeTrue(TestUtils.shouldTestSApis()); diff --git a/tests/unit/java/com/android/server/connectivityservice/CSKeepConnectedTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSKeepConnectedTest.kt index a9b9896a0944adfec22e7b0d14420018845f8d16..86426c20660505618bf02fd3579e9dada5b94201 100644 --- a/tests/unit/java/com/android/server/connectivityservice/CSKeepConnectedTest.kt +++ b/tests/unit/java/com/android/server/connectivityservice/CSKeepConnectedTest.kt @@ -16,8 +16,12 @@ package com.android.server +import android.net.NetworkCapabilities +import android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK +import android.net.NetworkCapabilities.TRANSPORT_WIFI import android.net.NetworkRequest import android.net.NetworkScore +import android.net.NetworkScore.KEEP_CONNECTED_DOWNSTREAM_NETWORK import android.net.NetworkScore.KEEP_CONNECTED_FOR_TEST import android.os.Build import androidx.test.filters.SmallTest @@ -32,6 +36,20 @@ import org.junit.runner.RunWith @SmallTest @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) class CSKeepConnectedTest : CSTest() { + @Test + fun testKeepConnectedLocalAgent() { + deps.setBuildSdk(VERSION_V) + val nc = NetworkCapabilities.Builder() + .addTransportType(TRANSPORT_WIFI) + .addCapability(NET_CAPABILITY_LOCAL_NETWORK) + .build() + val keepConnectedAgent = Agent(nc = nc, score = FromS(NetworkScore.Builder() + .setKeepConnectedReason(KEEP_CONNECTED_DOWNSTREAM_NETWORK) + .build())) + val dontKeepConnectedAgent = Agent(nc = nc) + doTestKeepConnected(keepConnectedAgent, dontKeepConnectedAgent) + } + @Test fun testKeepConnectedForTest() { val keepAgent = Agent(score = FromS(NetworkScore.Builder()