Reapply "Tell netd about local networks"
This reverts commit cc409153b48762ecf77d7fe182d0a5a7243a4794.
Change-Id: Ied6bdb9b94e08e2bfe1092765d0f4591e603c3eb
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 8475110..6aee6f8 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_FROZEN;
import static android.content.pm.PackageManager.FEATURE_BLUETOOTH;
+import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.content.pm.PackageManager.FEATURE_WIFI;
import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT;
@@ -67,6 +68,7 @@
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_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
@@ -5006,7 +5008,10 @@
!nai.networkAgentConfig.allowBypass /* secure */,
getVpnType(nai), nai.networkAgentConfig.excludeLocalRouteVpn);
} else {
- config = new NativeNetworkConfig(nai.network.getNetId(), NativeNetworkType.PHYSICAL,
+ final boolean hasLocalCap =
+ nai.networkCapabilities.hasCapability(NET_CAPABILITY_LOCAL_NETWORK);
+ config = new NativeNetworkConfig(nai.network.getNetId(),
+ hasLocalCap ? NativeNetworkType.PHYSICAL_LOCAL : NativeNetworkType.PHYSICAL,
getNetworkPermission(nai.networkCapabilities),
false /* secure */,
VpnManager.TYPE_VPN_NONE,
@@ -8057,6 +8062,18 @@
}
/**
+ * Returns whether local agents are supported on this device.
+ *
+ * Local agents are supported from U on TVs, and from V on all devices.
+ */
+ @VisibleForTesting
+ public boolean areLocalAgentsSupported() {
+ final PackageManager pm = mContext.getPackageManager();
+ // Local agents are supported starting on U on TVs and on V on everything else.
+ return mDeps.isAtLeastV() || (mDeps.isAtLeastU() && pm.hasSystemFeature(FEATURE_LEANBACK));
+ }
+
+ /**
* Register a new agent with ConnectivityService to handle a network.
*
* @param na a reference for ConnectivityService to contact the agent asynchronously.
@@ -8085,6 +8102,12 @@
} else {
enforceNetworkFactoryPermission();
}
+ final boolean hasLocalCap =
+ networkCapabilities.hasCapability(NET_CAPABILITY_LOCAL_NETWORK);
+ if (hasLocalCap && !areLocalAgentsSupported()) {
+ // Before U, netd doesn't support PHYSICAL_LOCAL networks so this can't work.
+ throw new IllegalArgumentException("Local agents are not supported in this version");
+ }
final int uid = mDeps.getCallingUid();
final long token = Binder.clearCallingIdentity();
@@ -9190,7 +9213,7 @@
// are Type.LISTEN, but should not have NetworkCallbacks invoked.
return;
}
- Bundle bundle = new Bundle();
+ final Bundle bundle = new Bundle();
// TODO b/177608132: make sure callbacks are indexed by NRIs and not NetworkRequest objects.
// TODO: check if defensive copies of data is needed.
final NetworkRequest nrForCallback = nri.getNetworkRequestForCallback();
diff --git a/service/src/com/android/server/connectivity/NetworkAgentInfo.java b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
index 0f72cd4..8d0d711 100644
--- a/service/src/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
@@ -64,7 +64,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.WakeupMessage;
-import com.android.modules.utils.build.SdkLevel;
import com.android.server.ConnectivityService;
import java.io.PrintWriter;
@@ -470,8 +469,8 @@
+ networkCapabilities.getOwnerUid() + " to " + nc.getOwnerUid());
nc.setOwnerUid(networkCapabilities.getOwnerUid());
}
- restrictCapabilitiesFromNetworkAgent(
- nc, creatorUid, mHasAutomotiveFeature, carrierPrivilegeAuthenticator);
+ restrictCapabilitiesFromNetworkAgent(nc, creatorUid, mHasAutomotiveFeature,
+ mConnServiceDeps, carrierPrivilegeAuthenticator);
return nc;
}
@@ -601,6 +600,7 @@
private static final String TAG = ConnectivityService.class.getSimpleName();
private static final boolean VDBG = false;
private final ConnectivityService mConnService;
+ private final ConnectivityService.Dependencies mConnServiceDeps;
private final Context mContext;
private final Handler mHandler;
private final QosCallbackTracker mQosCallbackTracker;
@@ -628,6 +628,7 @@
networkCapabilities = nc;
networkAgentConfig = config;
mConnService = connService;
+ mConnServiceDeps = deps;
setScore(score); // uses members connService, networkCapabilities and networkAgentConfig
clatd = new Nat464Xlat(this, netd, dnsResolver, deps);
mContext = context;
@@ -1518,23 +1519,26 @@
*/
public static void restrictCapabilitiesFromNetworkAgent(@NonNull final NetworkCapabilities nc,
final int creatorUid, final boolean hasAutomotiveFeature,
+ @NonNull final ConnectivityService.Dependencies deps,
@Nullable final CarrierPrivilegeAuthenticator authenticator) {
if (nc.hasTransport(TRANSPORT_TEST)) {
nc.restrictCapabilitiesForTestNetwork(creatorUid);
}
- if (!areAllowedUidsAcceptableFromNetworkAgent(nc, hasAutomotiveFeature, authenticator)) {
+ if (!areAllowedUidsAcceptableFromNetworkAgent(
+ nc, hasAutomotiveFeature, deps, authenticator)) {
nc.setAllowedUids(new ArraySet<>());
}
}
private static boolean areAllowedUidsAcceptableFromNetworkAgent(
@NonNull final NetworkCapabilities nc, final boolean hasAutomotiveFeature,
+ @NonNull final ConnectivityService.Dependencies deps,
@Nullable final CarrierPrivilegeAuthenticator carrierPrivilegeAuthenticator) {
// NCs without access UIDs are fine.
if (!nc.hasAllowedUids()) return true;
// S and below must never accept access UIDs, even if an agent sends them, because netd
// didn't support the required feature in S.
- if (!SdkLevel.isAtLeastT()) return false;
+ if (!deps.isAtLeastT()) return false;
// On a non-restricted network, access UIDs make no sense
if (nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) return false;
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentCreationTests.kt b/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentCreationTests.kt
new file mode 100644
index 0000000..7914e04
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentCreationTests.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server
+
+import android.content.pm.PackageManager.FEATURE_LEANBACK
+import android.net.INetd
+import android.net.NativeNetworkConfig
+import android.net.NativeNetworkType
+import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK
+import android.net.NetworkRequest
+import android.net.NetworkScore
+import android.net.NetworkScore.KEEP_CONNECTED_FOR_TEST
+import android.net.VpnManager
+import android.os.Build
+import androidx.test.filters.SmallTest
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.RecorderCallback.CallbackEntry.Available
+import com.android.testutils.TestableNetworkCallback
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.never
+import org.mockito.Mockito.timeout
+import kotlin.test.assertFailsWith
+
+private const val TIMEOUT_MS = 2_000L
+private const val NO_CALLBACK_TIMEOUT_MS = 200L
+
+private fun keepConnectedScore() =
+ FromS(NetworkScore.Builder().setKeepConnectedReason(KEEP_CONNECTED_FOR_TEST).build())
+
+@RunWith(DevSdkIgnoreRunner::class)
+@SmallTest
+@IgnoreUpTo(Build.VERSION_CODES.R)
+class CSLocalAgentCreationTests(
+ private val sdkLevel: Int,
+ private val isTv: Boolean,
+ private val addLocalNetCapToRequest: Boolean
+) : CSTest() {
+ companion object {
+ @JvmStatic
+ @Parameterized.Parameters
+ fun arguments() = listOf(
+ arrayOf(VERSION_V, false /* isTv */, true /* addLocalNetCapToRequest */),
+ arrayOf(VERSION_V, false /* isTv */, false /* addLocalNetCapToRequest */),
+ arrayOf(VERSION_V, true /* isTv */, true /* addLocalNetCapToRequest */),
+ arrayOf(VERSION_V, true /* isTv */, false /* addLocalNetCapToRequest */),
+ arrayOf(VERSION_U, false /* isTv */, true /* addLocalNetCapToRequest */),
+ arrayOf(VERSION_U, false /* isTv */, false /* addLocalNetCapToRequest */),
+ arrayOf(VERSION_U, true /* isTv */, true /* addLocalNetCapToRequest */),
+ arrayOf(VERSION_U, true /* isTv */, false /* addLocalNetCapToRequest */),
+ arrayOf(VERSION_T, false /* isTv */, false /* addLocalNetCapToRequest */),
+ arrayOf(VERSION_T, true /* isTv */, false /* addLocalNetCapToRequest */),
+ )
+ }
+
+ private fun makeNativeNetworkConfigLocal(netId: Int, permission: Int) =
+ NativeNetworkConfig(netId, NativeNetworkType.PHYSICAL_LOCAL, permission,
+ false /* secure */, VpnManager.TYPE_VPN_NONE, false /* excludeLocalRoutes */)
+
+ @Test
+ fun testLocalAgents() {
+ val netdInOrder = inOrder(netd)
+ deps.setBuildSdk(sdkLevel)
+ doReturn(isTv).`when`(packageManager).hasSystemFeature(FEATURE_LEANBACK)
+ val allNetworksCb = TestableNetworkCallback()
+ val request = NetworkRequest.Builder()
+ if (addLocalNetCapToRequest) {
+ request.addCapability(NET_CAPABILITY_LOCAL_NETWORK)
+ }
+ cm.registerNetworkCallback(request.build(), allNetworksCb)
+ val ncTemplate = NetworkCapabilities.Builder().run {
+ addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ addCapability(NET_CAPABILITY_LOCAL_NETWORK)
+ }.build()
+ val localAgent = if (sdkLevel >= VERSION_V || sdkLevel == VERSION_U && isTv) {
+ Agent(nc = ncTemplate, score = keepConnectedScore())
+ } else {
+ assertFailsWith<IllegalArgumentException> { Agent(nc = ncTemplate) }
+ netdInOrder.verify(netd, never()).networkCreate(any())
+ return
+ }
+ localAgent.connect()
+ netdInOrder.verify(netd).networkCreate(
+ makeNativeNetworkConfigLocal(localAgent.network.netId, INetd.PERMISSION_NONE))
+ if (addLocalNetCapToRequest) {
+ assertEquals(localAgent.network, allNetworksCb.expect<Available>().network)
+ } else {
+ allNetworksCb.assertNoCallback(NO_CALLBACK_TIMEOUT_MS)
+ }
+ cm.unregisterNetworkCallback(allNetworksCb)
+ localAgent.disconnect()
+ netdInOrder.verify(netd, timeout(TIMEOUT_MS)).networkDestroy(localAgent.network.netId)
+ }
+}