Send access UIDs to netd
Test: FrameworkNetTests CtsNetTestCases
Change-Id: I8301abaddf5850071fa23d41e8e736ab7071e299
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index 26fef94..a39e192 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -1865,6 +1865,13 @@
return new ArraySet<>(mAccessUids);
}
+ /** @hide */
+ // For internal clients that know what they are doing and need to avoid the performance hit
+ // of the defensive copy.
+ public @NonNull ArraySet<Integer> getAccessUidsNoCopy() {
+ return mAccessUids;
+ }
+
/**
* Test whether this UID has special permission to access this network, as per mAccessUids.
* @hide
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index f1c1499..f587cf0 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -439,8 +439,6 @@
* Requests that don't code for a per-app preference use PREFERENCE_ORDER_INVALID.
* The default request uses PREFERENCE_ORDER_DEFAULT.
*/
- // Bound for the lowest valid preference order.
- static final int PREFERENCE_ORDER_LOWEST = 999;
// Used when sending to netd to code for "no order".
static final int PREFERENCE_ORDER_NONE = 0;
// Order for requests that don't code for a per-app preference. As it is
@@ -448,11 +446,6 @@
// PREFERENCE_ORDER_NONE when sending to netd.
@VisibleForTesting
static final int PREFERENCE_ORDER_INVALID = Integer.MAX_VALUE;
- // Order for the default internet request. Since this must always have the
- // lowest priority, its value is larger than the largest acceptable value. As
- // it is out of the valid range, the corresponding order should be
- // PREFERENCE_ORDER_NONE when sending to netd.
- static final int PREFERENCE_ORDER_DEFAULT = 1000;
// As a security feature, VPNs have the top priority.
static final int PREFERENCE_ORDER_VPN = 0; // Netd supports only 0 for VPN.
// Order of per-app OEM preference. See {@link #setOemNetworkPreference}.
@@ -467,6 +460,13 @@
// See {@link ConnectivitySettingsManager#setMobileDataPreferredUids}
@VisibleForTesting
static final int PREFERENCE_ORDER_MOBILE_DATA_PREFERERRED = 30;
+ // Preference order that signifies the network shouldn't be set as a default network for
+ // the UIDs, only give them access to it. TODO : replace this with a boolean
+ // in NativeUidRangeConfig
+ @VisibleForTesting
+ static final int PREFERENCE_ORDER_IRRELEVANT_BECAUSE_NOT_DEFAULT = 999;
+ // Bound for the lowest valid preference order.
+ static final int PREFERENCE_ORDER_LOWEST = 999;
/**
* used internally to clear a wakelock when transitioning
@@ -7749,6 +7749,17 @@
return stableRanges;
}
+ private static UidRangeParcel[] intsToUidRangeStableParcels(
+ final @NonNull ArraySet<Integer> uids) {
+ final UidRangeParcel[] stableRanges = new UidRangeParcel[uids.size()];
+ int index = 0;
+ for (int uid : uids) {
+ stableRanges[index] = new UidRangeParcel(uid, uid);
+ index++;
+ }
+ return stableRanges;
+ }
+
private static UidRangeParcel[] toUidRangeStableParcels(UidRange[] ranges) {
final UidRangeParcel[] stableRanges = new UidRangeParcel[ranges.length];
for (int i = 0; i < ranges.length; i++) {
@@ -7819,8 +7830,14 @@
}
}
- private void updateUids(NetworkAgentInfo nai, NetworkCapabilities prevNc,
- NetworkCapabilities newNc) {
+ private void updateUids(@NonNull NetworkAgentInfo nai, @Nullable NetworkCapabilities prevNc,
+ @Nullable NetworkCapabilities newNc) {
+ updateVpnUids(nai, prevNc, newNc);
+ updateAccessUids(nai, prevNc, newNc);
+ }
+
+ private void updateVpnUids(@NonNull NetworkAgentInfo nai, @Nullable NetworkCapabilities prevNc,
+ @Nullable NetworkCapabilities newNc) {
Set<UidRange> prevRanges = null == prevNc ? null : prevNc.getUidRanges();
Set<UidRange> newRanges = null == newNc ? null : newNc.getUidRanges();
if (null == prevRanges) prevRanges = new ArraySet<>();
@@ -7879,6 +7896,46 @@
}
}
+ private void updateAccessUids(@NonNull NetworkAgentInfo nai,
+ @Nullable NetworkCapabilities prevNc, @Nullable NetworkCapabilities newNc) {
+ // In almost all cases both NC code for empty access UIDs. return as fast as possible.
+ final boolean prevEmpty = null == prevNc || prevNc.getAccessUidsNoCopy().isEmpty();
+ final boolean newEmpty = null == newNc || newNc.getAccessUidsNoCopy().isEmpty();
+ if (prevEmpty && newEmpty) return;
+
+ final ArraySet<Integer> prevUids =
+ null == prevNc ? new ArraySet<>() : prevNc.getAccessUidsNoCopy();
+ final ArraySet<Integer> newUids =
+ null == newNc ? new ArraySet<>() : newNc.getAccessUidsNoCopy();
+
+ if (prevUids.equals(newUids)) return;
+
+ // This implementation is very simple and vastly faster for sets of Integers than
+ // CompareOrUpdateResult, which is tuned for sets that need to be compared based on
+ // a key computed from the value and has storage for that.
+ final ArraySet<Integer> toRemove = new ArraySet<>(prevUids);
+ final ArraySet<Integer> toAdd = new ArraySet<>(newUids);
+ toRemove.removeAll(newUids);
+ toAdd.removeAll(prevUids);
+
+ try {
+ if (!toAdd.isEmpty()) {
+ mNetd.networkAddUidRangesParcel(new NativeUidRangeConfig(
+ nai.network.netId,
+ intsToUidRangeStableParcels(toAdd),
+ PREFERENCE_ORDER_IRRELEVANT_BECAUSE_NOT_DEFAULT));
+ }
+ if (!toRemove.isEmpty()) {
+ mNetd.networkRemoveUidRangesParcel(new NativeUidRangeConfig(
+ nai.network.netId,
+ intsToUidRangeStableParcels(toRemove),
+ PREFERENCE_ORDER_IRRELEVANT_BECAUSE_NOT_DEFAULT));
+ }
+ } catch (RemoteException e) {
+ // Netd died. This usually causes a runtime restart anyway.
+ }
+ }
+
public void handleUpdateLinkProperties(NetworkAgentInfo nai, LinkProperties newLp) {
ensureRunningOnConnectivityServiceThread();
diff --git a/service/src/com/android/server/connectivity/NetworkAgentInfo.java b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
index 76eaf22..b9e2a5f 100644
--- a/service/src/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
@@ -17,6 +17,7 @@
package com.android.server.connectivity;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.NetworkCapabilities.transportNamesOf;
@@ -1208,12 +1209,18 @@
private static boolean areAccessUidsAcceptableFromNetworkAgent(
@NonNull final NetworkCapabilities nc) {
- if (nc.hasAccessUids()) {
- Log.w(TAG, "Capabilities from network agent must not contain access UIDs");
- // TODO : accept the supported cases
- return false;
- }
- return true;
+ // NCs without access UIDs are fine.
+ if (!nc.hasAccessUids()) return true;
+
+ // On a non-restricted network, access UIDs make no sense
+ if (nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) return false;
+
+ // If this network has TRANSPORT_TEST, then the caller can do whatever they want to
+ // access UIDs
+ if (nc.hasTransport(TRANSPORT_TEST)) return true;
+
+ // TODO : accept more supported cases
+ return false;
}
// TODO: Print shorter members first and only print the boolean variable which value is true
diff --git a/tests/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/integration/util/com/android/server/NetworkAgentWrapper.java
index 4dc86ff..365c0cf 100644
--- a/tests/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -21,6 +21,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
@@ -109,6 +110,9 @@
case TRANSPORT_WIFI_AWARE:
mScore = new NetworkScore.Builder().setLegacyInt(20).build();
break;
+ case TRANSPORT_TEST:
+ mScore = new NetworkScore.Builder().build();
+ break;
case TRANSPORT_VPN:
mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_VPN);
// VPNs deduce the SUSPENDED capability from their underlying networks and there
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index f7ffd79..2985c41 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -23,6 +23,7 @@
import static android.Manifest.permission.DUMP;
import static android.Manifest.permission.GET_INTENT_SENDER_INTENT;
import static android.Manifest.permission.LOCAL_MAC_ADDRESS;
+import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
import static android.Manifest.permission.NETWORK_FACTORY;
import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.Manifest.permission.NETWORK_STACK;
@@ -108,6 +109,7 @@
import static android.net.NetworkCapabilities.REDACT_NONE;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
@@ -1490,6 +1492,10 @@
r -> new UidRangeParcel(r.start, r.stop)).toArray(UidRangeParcel[]::new);
}
+ private UidRangeParcel[] intToUidRangeStableParcels(final @NonNull Set<Integer> ranges) {
+ return ranges.stream().map(r -> new UidRangeParcel(r, r)).toArray(UidRangeParcel[]::new);
+ }
+
private VpnManagerService makeVpnManagerService() {
final VpnManagerService.Dependencies deps = new VpnManagerService.Dependencies() {
public int getCallingUid() {
@@ -14601,6 +14607,75 @@
() -> mCm.registerNetworkCallback(getRequestWithSubIds(), new NetworkCallback()));
}
+ @Test
+ public void testAccessUids() throws Exception {
+ final int preferenceOrder =
+ ConnectivityService.PREFERENCE_ORDER_IRRELEVANT_BECAUSE_NOT_DEFAULT;
+ mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_GRANTED);
+ mServiceContext.setPermission(MANAGE_TEST_NETWORKS, PERMISSION_GRANTED);
+ final TestNetworkCallback cb = new TestNetworkCallback();
+ mCm.requestNetwork(new NetworkRequest.Builder()
+ .clearCapabilities()
+ .addTransportType(TRANSPORT_TEST)
+ .build(),
+ cb);
+
+ final ArraySet<Integer> uids = new ArraySet<>();
+ uids.add(200);
+ final NetworkCapabilities nc = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_TEST)
+ .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ .setAccessUids(uids)
+ .build();
+ final TestNetworkAgentWrapper agent = new TestNetworkAgentWrapper(TRANSPORT_TEST,
+ new LinkProperties(), nc);
+ agent.connect(true);
+ cb.expectAvailableThenValidatedCallbacks(agent);
+
+ final InOrder inOrder = inOrder(mMockNetd);
+ final NativeUidRangeConfig uids200Parcel = new NativeUidRangeConfig(
+ agent.getNetwork().getNetId(),
+ intToUidRangeStableParcels(uids),
+ preferenceOrder);
+ inOrder.verify(mMockNetd, times(1)).networkAddUidRangesParcel(uids200Parcel);
+
+ uids.add(300);
+ uids.add(400);
+ nc.setAccessUids(uids);
+ agent.setNetworkCapabilities(nc, true /* sendToConnectivityService */);
+ cb.expectCapabilitiesThat(agent, caps -> caps.getAccessUids().equals(uids));
+
+ uids.remove(200);
+ final NativeUidRangeConfig uids300400Parcel = new NativeUidRangeConfig(
+ agent.getNetwork().getNetId(),
+ intToUidRangeStableParcels(uids),
+ preferenceOrder);
+ inOrder.verify(mMockNetd, times(1)).networkAddUidRangesParcel(uids300400Parcel);
+
+ nc.setAccessUids(uids);
+ agent.setNetworkCapabilities(nc, true /* sendToConnectivityService */);
+ cb.expectCapabilitiesThat(agent, caps -> caps.getAccessUids().equals(uids));
+ inOrder.verify(mMockNetd, times(1)).networkRemoveUidRangesParcel(uids200Parcel);
+
+ uids.clear();
+ uids.add(600);
+ nc.setAccessUids(uids);
+ agent.setNetworkCapabilities(nc, true /* sendToConnectivityService */);
+ cb.expectCapabilitiesThat(agent, caps -> caps.getAccessUids().equals(uids));
+ final NativeUidRangeConfig uids600Parcel = new NativeUidRangeConfig(
+ agent.getNetwork().getNetId(),
+ intToUidRangeStableParcels(uids),
+ preferenceOrder);
+ inOrder.verify(mMockNetd, times(1)).networkAddUidRangesParcel(uids600Parcel);
+ inOrder.verify(mMockNetd, times(1)).networkRemoveUidRangesParcel(uids300400Parcel);
+
+ uids.clear();
+ nc.setAccessUids(uids);
+ agent.setNetworkCapabilities(nc, true /* sendToConnectivityService */);
+ cb.expectCapabilitiesThat(agent, caps -> caps.getAccessUids().isEmpty());
+ inOrder.verify(mMockNetd, times(1)).networkRemoveUidRangesParcel(uids600Parcel);
+ }
+
/**
* Validate request counts are counted accurately on setProfileNetworkPreference on set/replace.
*/