Merge changes Id5e5b911,I8301abad,I7262811a,I658e579b,I7d43ef0e am: 4fcb8ad292
Original change: https://android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/1958935
Change-Id: I689801db7f5ea93167f62a93ee6c8f4ff89533aa
diff --git a/framework/api/module-lib-current.txt b/framework/api/module-lib-current.txt
index 8da421d..5cfab5d 100644
--- a/framework/api/module-lib-current.txt
+++ b/framework/api/module-lib-current.txt
@@ -132,6 +132,7 @@
}
public final class NetworkCapabilities implements android.os.Parcelable {
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public java.util.Set<java.lang.Integer> getAccessUids();
method @Nullable public java.util.Set<android.util.Range<java.lang.Integer>> getUids();
method public boolean hasForbiddenCapability(int);
field public static final long REDACT_ALL = -1L; // 0xffffffffffffffffL
@@ -143,6 +144,7 @@
}
public static final class NetworkCapabilities.Builder {
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setAccessUids(@NonNull java.util.Set<java.lang.Integer>);
method @NonNull public android.net.NetworkCapabilities.Builder setUids(@Nullable java.util.Set<android.util.Range<java.lang.Integer>>);
}
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index 4ae3a06..4254a66 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -264,6 +264,7 @@
mTransportInfo = null;
mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED;
mUids = null;
+ mAccessUids.clear();
mAdministratorUids = new int[0];
mOwnerUid = Process.INVALID_UID;
mSSID = null;
@@ -294,6 +295,7 @@
}
mSignalStrength = nc.mSignalStrength;
mUids = (nc.mUids == null) ? null : new ArraySet<>(nc.mUids);
+ setAccessUids(nc.mAccessUids);
setAdministratorUids(nc.getAdministratorUids());
mOwnerUid = nc.mOwnerUid;
mForbiddenNetworkCapabilities = nc.mForbiddenNetworkCapabilities;
@@ -1025,6 +1027,7 @@
final int[] originalAdministratorUids = getAdministratorUids();
final TransportInfo originalTransportInfo = getTransportInfo();
final Set<Integer> originalSubIds = getSubscriptionIds();
+ final Set<Integer> originalAccessUids = new ArraySet<>(mAccessUids);
clearAll();
if (0 != (originalCapabilities & (1 << NET_CAPABILITY_NOT_RESTRICTED))) {
// If the test network is not restricted, then it is only allowed to declare some
@@ -1044,6 +1047,7 @@
mNetworkSpecifier = originalSpecifier;
mSignalStrength = originalSignalStrength;
mTransportInfo = originalTransportInfo;
+ mAccessUids.addAll(originalAccessUids);
// Only retain the owner and administrator UIDs if they match the app registering the remote
// caller that registered the network.
@@ -1809,6 +1813,86 @@
}
/**
+ * List of UIDs that can always access this network.
+ * <p>
+ * UIDs in this list have access to this network, even if the network doesn't have the
+ * {@link #NET_CAPABILITY_NOT_RESTRICTED} capability and the UID does not hold the
+ * {@link android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS} permission.
+ * This is only useful for restricted networks. For non-restricted networks it has no effect.
+ * <p>
+ * This is disallowed in {@link NetworkRequest}, and can only be set by network agents. Network
+ * agents also have restrictions on how they can set these ; they can only back a public
+ * Android API. As such, Ethernet agents can set this when backing the per-UID access API, and
+ * Telephony can set exactly one UID which has to match the manager app for the associated
+ * subscription. Failure to comply with these rules will see this member cleared.
+ * <p>
+ * This member is never null, but can be empty.
+ * @hide
+ */
+ @NonNull
+ private final ArraySet<Integer> mAccessUids = new ArraySet<>();
+
+ /**
+ * Set the list of UIDs that can always access this network.
+ * @param uids
+ * @hide
+ */
+ public void setAccessUids(@NonNull final Set<Integer> uids) {
+ // could happen with nc.set(nc), cheaper than always making a defensive copy
+ if (uids == mAccessUids) return;
+
+ Objects.requireNonNull(uids);
+ mAccessUids.clear();
+ mAccessUids.addAll(uids);
+ }
+
+ /**
+ * The list of UIDs that can always access this network.
+ *
+ * The UIDs in this list can always access this network, even if it is restricted and
+ * the UID doesn't hold the USE_RESTRICTED_NETWORKS permission. This is defined by the
+ * network agent in charge of creating the network.
+ *
+ * The UIDs are only visible to network factories and the system server, since the system
+ * server makes sure to redact them before sending a NetworkCapabilities to a process
+ * that doesn't hold the permission.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ public @NonNull Set<Integer> getAccessUids() {
+ 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
+ */
+ public boolean isAccessUid(int uid) {
+ return mAccessUids.contains(uid);
+ }
+
+ /**
+ * @return whether any UID is in the list of access UIDs
+ * @hide
+ */
+ public boolean hasAccessUids() {
+ return !mAccessUids.isEmpty();
+ }
+
+ private boolean equalsAccessUids(@NonNull NetworkCapabilities other) {
+ return mAccessUids.equals(other.mAccessUids);
+ }
+
+ /**
* The SSID of the network, or null if not applicable or unknown.
* <p>
* This is filled in by wifi code.
@@ -1962,6 +2046,7 @@
&& equalsSpecifier(that)
&& equalsTransportInfo(that)
&& equalsUids(that)
+ && equalsAccessUids(that)
&& equalsSSID(that)
&& equalsOwnerUid(that)
&& equalsPrivateDnsBroken(that)
@@ -1986,15 +2071,16 @@
+ mSignalStrength * 29
+ mOwnerUid * 31
+ Objects.hashCode(mUids) * 37
- + Objects.hashCode(mSSID) * 41
- + Objects.hashCode(mTransportInfo) * 43
- + Objects.hashCode(mPrivateDnsBroken) * 47
- + Objects.hashCode(mRequestorUid) * 53
- + Objects.hashCode(mRequestorPackageName) * 59
- + Arrays.hashCode(mAdministratorUids) * 61
- + Objects.hashCode(mSubIds) * 67
- + Objects.hashCode(mUnderlyingNetworks) * 71
- + mEnterpriseId * 73;
+ + Objects.hashCode(mAccessUids) * 41
+ + Objects.hashCode(mSSID) * 43
+ + Objects.hashCode(mTransportInfo) * 47
+ + Objects.hashCode(mPrivateDnsBroken) * 53
+ + Objects.hashCode(mRequestorUid) * 59
+ + Objects.hashCode(mRequestorPackageName) * 61
+ + Arrays.hashCode(mAdministratorUids) * 67
+ + Objects.hashCode(mSubIds) * 71
+ + Objects.hashCode(mUnderlyingNetworks) * 73
+ + mEnterpriseId * 79;
}
@Override
@@ -2022,6 +2108,7 @@
dest.writeParcelable((Parcelable) mTransportInfo, flags);
dest.writeInt(mSignalStrength);
writeParcelableArraySet(dest, mUids, flags);
+ dest.writeIntArray(CollectionUtils.toIntArray(mAccessUids));
dest.writeString(mSSID);
dest.writeBoolean(mPrivateDnsBroken);
dest.writeIntArray(getAdministratorUids());
@@ -2034,7 +2121,7 @@
}
public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
- new Creator<NetworkCapabilities>() {
+ new Creator<>() {
@Override
public NetworkCapabilities createFromParcel(Parcel in) {
NetworkCapabilities netCap = new NetworkCapabilities();
@@ -2048,6 +2135,11 @@
netCap.mTransportInfo = in.readParcelable(null);
netCap.mSignalStrength = in.readInt();
netCap.mUids = readParcelableArraySet(in, null /* ClassLoader, null for default */);
+ final int[] accessUids = in.createIntArray();
+ netCap.mAccessUids.ensureCapacity(accessUids.length);
+ for (int uid : accessUids) {
+ netCap.mAccessUids.add(uid);
+ }
netCap.mSSID = in.readString();
netCap.mPrivateDnsBroken = in.readBoolean();
netCap.setAdministratorUids(in.createIntArray());
@@ -2124,6 +2216,11 @@
sb.append(" Uids: <").append(mUids).append(">");
}
}
+
+ if (hasAccessUids()) {
+ sb.append(" AccessUids: <").append(mAccessUids).append(">");
+ }
+
if (mOwnerUid != Process.INVALID_UID) {
sb.append(" OwnerUid: ").append(mOwnerUid);
}
@@ -2300,7 +2397,7 @@
private static void checkValidCapability(@NetworkCapabilities.NetCapability int capability) {
if (!isValidCapability(capability)) {
- throw new IllegalArgumentException("NetworkCapability " + capability + "out of range");
+ throw new IllegalArgumentException("NetworkCapability " + capability + " out of range");
}
}
@@ -2909,6 +3006,44 @@
}
/**
+ * Set a list of UIDs that can always access this network
+ * <p>
+ * Provide a list of UIDs that can access this network even if the network doesn't have the
+ * {@link #NET_CAPABILITY_NOT_RESTRICTED} capability and the UID does not hold the
+ * {@link android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS} permission.
+ * <p>
+ * This is disallowed in {@link NetworkRequest}, and can only be set by
+ * {@link NetworkAgent}s, who hold the
+ * {@link android.Manifest.permission.NETWORK_FACTORY} permission.
+ * Network agents also have restrictions on how they can set these ; they can only back
+ * a public Android API. As such, Ethernet agents can set this when backing the per-UID
+ * access API, and Telephony can set exactly one UID which has to match the manager app for
+ * the associated subscription. Failure to comply with these rules will see this member
+ * cleared.
+ * <p>
+ * These UIDs are only visible to network factories and the system server, since the system
+ * server makes sure to redact them before sending a {@link NetworkCapabilities} instance
+ * to a process that doesn't hold the {@link android.Manifest.permission.NETWORK_FACTORY}
+ * permission.
+ * <p>
+ * This list cannot be null, but it can be empty to mean that no UID without the
+ * {@link android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS} permission
+ * gets to access this network.
+ *
+ * @param uids the list of UIDs that can always access this network
+ * @return this builder
+ * @hide
+ */
+ @NonNull
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ public Builder setAccessUids(@NonNull Set<Integer> uids) {
+ Objects.requireNonNull(uids);
+ mCaps.setAccessUids(uids);
+ return this;
+ }
+
+ /**
* Set the underlying networks of this network.
*
* @param networks The underlying networks of this network.
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 8137551..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
@@ -2099,6 +2099,7 @@
newNc.setAdministratorUids(new int[0]);
if (!checkAnyPermissionOf(
callerPid, callerUid, android.Manifest.permission.NETWORK_FACTORY)) {
+ newNc.setAccessUids(new ArraySet<>());
newNc.setSubscriptionIds(Collections.emptySet());
}
@@ -3348,18 +3349,8 @@
switch (msg.what) {
case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
- NetworkCapabilities networkCapabilities = (NetworkCapabilities) arg.second;
- if (networkCapabilities.hasConnectivityManagedCapability()) {
- Log.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
- }
- if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
- // Make sure the original object is not mutated. NetworkAgent normally
- // makes a copy of the capabilities when sending the message through
- // the Messenger, but if this ever changes, not making a defensive copy
- // here will give attack vectors to clients using this code path.
- networkCapabilities = new NetworkCapabilities(networkCapabilities);
- networkCapabilities.restrictCapabilitiesForTestNetwork(nai.creatorUid);
- }
+ final NetworkCapabilities networkCapabilities = new NetworkCapabilities(
+ (NetworkCapabilities) arg.second);
processCapabilitiesFromAgent(nai, networkCapabilities);
updateCapabilities(nai.getCurrentScore(), nai, networkCapabilities);
break;
@@ -6220,6 +6211,9 @@
if (nc.isPrivateDnsBroken()) {
throw new IllegalArgumentException("Can't request broken private DNS");
}
+ if (nc.hasAccessUids()) {
+ throw new IllegalArgumentException("Can't request access UIDs");
+ }
}
// TODO: Set the mini sdk to 31 and remove @TargetApi annotation when b/205923322 is addressed.
@@ -6454,9 +6448,10 @@
ensureRequestableCapabilities(networkCapabilities);
ensureSufficientPermissionsForRequest(networkCapabilities,
Binder.getCallingPid(), callingUid, callingPackageName);
- ensureValidNetworkSpecifier(networkCapabilities);
restrictRequestUidsForCallerAndSetRequestorInfo(networkCapabilities,
callingUid, callingPackageName);
+ ensureValid(networkCapabilities);
+
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
nextNetworkRequestId(), NetworkRequest.Type.REQUEST);
NetworkRequestInfo nri = new NetworkRequestInfo(callingUid, networkRequest, operation,
@@ -6976,28 +6971,18 @@
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
NetworkScore currentScore, NetworkAgentConfig networkAgentConfig, int providerId,
int uid) {
- if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
- // Strictly, sanitizing here is unnecessary as the capabilities will be sanitized in
- // the call to mixInCapabilities below anyway, but sanitizing here means the NAI never
- // sees capabilities that may be malicious, which might prevent mistakes in the future.
- networkCapabilities = new NetworkCapabilities(networkCapabilities);
- networkCapabilities.restrictCapabilitiesForTestNetwork(uid);
- }
- LinkProperties lp = new LinkProperties(linkProperties);
-
- final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
+ // At this point the capabilities/properties are untrusted and unverified, e.g. checks that
+ // the capabilities' access UID comply with security limitations. They will be sanitized
+ // as the NAI registration finishes, in handleRegisterNetworkAgent(). This is
+ // because some of the checks must happen on the handler thread.
final NetworkAgentInfo nai = new NetworkAgentInfo(na,
- new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
+ new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo),
+ linkProperties, networkCapabilities,
currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig),
this, mNetd, mDnsResolver, providerId, uid, mLingerDelayMs,
mQosCallbackTracker, mDeps);
- // Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says.
- processCapabilitiesFromAgent(nai, nc);
- nai.getAndSetNetworkCapabilities(mixInCapabilities(nai, nc));
- processLinkPropertiesFromAgent(nai, nai.linkProperties);
-
final String extraInfo = networkInfo.getExtraInfo();
final String name = TextUtils.isEmpty(extraInfo)
? nai.networkCapabilities.getSsid() : extraInfo;
@@ -7012,8 +6997,20 @@
}
private void handleRegisterNetworkAgent(NetworkAgentInfo nai, INetworkMonitor networkMonitor) {
+ if (VDBG) log("Network Monitor created for " + nai);
+ // nai.nc and nai.lp are the same object that was passed by the network agent if the agent
+ // lives in the same process as this code (e.g. wifi), so make sure this code doesn't
+ // mutate their object
+ final NetworkCapabilities nc = new NetworkCapabilities(nai.networkCapabilities);
+ final LinkProperties lp = new LinkProperties(nai.linkProperties);
+ // Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says.
+ processCapabilitiesFromAgent(nai, nc);
+ nai.getAndSetNetworkCapabilities(mixInCapabilities(nai, nc));
+ processLinkPropertiesFromAgent(nai, lp);
+ nai.linkProperties = lp;
+
nai.onNetworkMonitorCreated(networkMonitor);
- if (VDBG) log("Got NetworkAgent Messenger");
+
mNetworkAgentInfos.add(nai);
synchronized (mNetworkForNetId) {
mNetworkForNetId.put(nai.network.getNetId(), nai);
@@ -7024,6 +7021,7 @@
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
+
nai.notifyRegistered();
NetworkInfo networkInfo = nai.networkInfo;
updateNetworkInfo(nai, networkInfo);
@@ -7471,9 +7469,11 @@
* Stores into |nai| any data coming from the agent that might also be written to the network's
* NetworkCapabilities by ConnectivityService itself. This ensures that the data provided by the
* agent is not lost when updateCapabilities is called.
- * This method should never alter the agent's NetworkCapabilities, only store data in |nai|.
*/
private void processCapabilitiesFromAgent(NetworkAgentInfo nai, NetworkCapabilities nc) {
+ if (nc.hasConnectivityManagedCapability()) {
+ Log.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
+ }
// Note: resetting the owner UID before storing the agent capabilities in NAI means that if
// the agent attempts to change the owner UID, then nai.declaredCapabilities will not
// actually be the same as the capabilities sent by the agent. Still, it is safer to reset
@@ -7484,6 +7484,7 @@
nc.setOwnerUid(nai.networkCapabilities.getOwnerUid());
}
nai.declaredCapabilities = new NetworkCapabilities(nc);
+ NetworkAgentInfo.restrictCapabilitiesFromNetworkAgent(nc, nai.creatorUid);
}
/** Modifies |newNc| based on the capabilities of |underlyingNetworks| and |agentCaps|. */
@@ -7748,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++) {
@@ -7818,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<>();
@@ -7878,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();
@@ -9844,7 +9902,7 @@
android.Manifest.permission.NETWORK_STACK);
final NetworkCapabilities nc = getNetworkCapabilitiesInternal(network);
if (!nc.hasTransport(TRANSPORT_TEST)) {
- throw new SecurityException("Data Stall simluation is only possible for test networks");
+ throw new SecurityException("Data Stall simulation is only possible for test networks");
}
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
diff --git a/service/src/com/android/server/connectivity/NetworkAgentInfo.java b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
index 1a7248a..b9e2a5f 100644
--- a/service/src/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
@@ -17,7 +17,9 @@
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;
import android.annotation.NonNull;
@@ -52,6 +54,7 @@
import android.os.SystemClock;
import android.telephony.data.EpsBearerQosSessionAttributes;
import android.telephony.data.NrQosSessionAttributes;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
@@ -1188,6 +1191,38 @@
return mConnectivityReport;
}
+ /**
+ * Make sure the NC from network agents don't contain stuff they shouldn't.
+ *
+ * @param nc the capabilities to sanitize
+ * @param creatorUid the UID of the process creating this network agent
+ */
+ public static void restrictCapabilitiesFromNetworkAgent(@NonNull final NetworkCapabilities nc,
+ final int creatorUid) {
+ if (nc.hasTransport(TRANSPORT_TEST)) {
+ nc.restrictCapabilitiesForTestNetwork(creatorUid);
+ }
+ if (!areAccessUidsAcceptableFromNetworkAgent(nc)) {
+ nc.setAccessUids(new ArraySet<>());
+ }
+ }
+
+ private static boolean areAccessUidsAcceptableFromNetworkAgent(
+ @NonNull final NetworkCapabilities nc) {
+ // 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
// to improve readability.
public String toString() {
diff --git a/tests/common/java/android/net/NetworkCapabilitiesTest.java b/tests/common/java/android/net/NetworkCapabilitiesTest.java
index bea00a9..742044b 100644
--- a/tests/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/common/java/android/net/NetworkCapabilitiesTest.java
@@ -308,6 +308,48 @@
}
}
+ @Test @IgnoreUpTo(SC_V2)
+ public void testSetAccessUids() {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ assertThrows(NullPointerException.class, () -> nc.setAccessUids(null));
+ assertFalse(nc.hasAccessUids());
+ assertFalse(nc.isAccessUid(0));
+ assertFalse(nc.isAccessUid(1000));
+ assertEquals(0, nc.getAccessUids().size());
+ nc.setAccessUids(new ArraySet<>());
+ assertFalse(nc.hasAccessUids());
+ assertFalse(nc.isAccessUid(0));
+ assertFalse(nc.isAccessUid(1000));
+ assertEquals(0, nc.getAccessUids().size());
+
+ final ArraySet<Integer> uids = new ArraySet<>();
+ uids.add(200);
+ uids.add(250);
+ uids.add(-1);
+ uids.add(Integer.MAX_VALUE);
+ nc.setAccessUids(uids);
+ assertNotEquals(nc, new NetworkCapabilities());
+ assertTrue(nc.hasAccessUids());
+
+ final List<Integer> includedList = List.of(-2, 0, 199, 700, 901, 1000, Integer.MIN_VALUE);
+ final List<Integer> excludedList = List.of(-1, 200, 250, Integer.MAX_VALUE);
+ for (final int uid : includedList) {
+ assertFalse(nc.isAccessUid(uid));
+ }
+ for (final int uid : excludedList) {
+ assertTrue(nc.isAccessUid(uid));
+ }
+
+ final Set<Integer> outUids = nc.getAccessUids();
+ assertEquals(4, outUids.size());
+ for (final int uid : includedList) {
+ assertFalse(outUids.contains(uid));
+ }
+ for (final int uid : excludedList) {
+ assertTrue(outUids.contains(uid));
+ }
+ }
+
@Test
public void testParcelNetworkCapabilities() {
final Set<Range<Integer>> uids = new ArraySet<>();
@@ -318,6 +360,10 @@
.addCapability(NET_CAPABILITY_EIMS)
.addCapability(NET_CAPABILITY_NOT_METERED);
if (isAtLeastS()) {
+ final ArraySet<Integer> accessUids = new ArraySet<>();
+ accessUids.add(4);
+ accessUids.add(9);
+ netCap.setAccessUids(accessUids);
netCap.setSubscriptionIds(Set.of(TEST_SUBID1, TEST_SUBID2));
netCap.setUids(uids);
}
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index ef5dc77..344482b 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -23,7 +23,6 @@
import android.net.INetworkAgentRegistry
import android.net.InetAddresses
import android.net.IpPrefix
-import android.net.KeepalivePacketData
import android.net.LinkAddress
import android.net.LinkProperties
import android.net.NattKeepalivePacketData
@@ -42,6 +41,7 @@
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN
import android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED
import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED
+import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
import android.net.NetworkCapabilities.TRANSPORT_TEST
import android.net.NetworkCapabilities.TRANSPORT_VPN
import android.net.NetworkInfo
@@ -53,7 +53,6 @@
import android.net.QosCallback
import android.net.QosCallbackException
import android.net.QosCallback.QosCallbackRegistrationException
-import android.net.QosFilter
import android.net.QosSession
import android.net.QosSessionAttributes
import android.net.QosSocketInfo
@@ -67,7 +66,6 @@
import android.os.Build
import android.os.Handler
import android.os.HandlerThread
-import android.os.Looper
import android.os.Message
import android.os.SystemClock
import android.telephony.TelephonyManager
@@ -82,6 +80,8 @@
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.RecorderCallback.CallbackEntry.Available
+import com.android.testutils.RecorderCallback.CallbackEntry.BlockedStatus
+import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
import com.android.testutils.RecorderCallback.CallbackEntry.Losing
import com.android.testutils.RecorderCallback.CallbackEntry.Lost
import com.android.testutils.TestableNetworkAgent
@@ -100,7 +100,6 @@
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnValidationStatus
import com.android.testutils.TestableNetworkCallback
import org.junit.After
-import org.junit.Assert.assertArrayEquals
import org.junit.Assume.assumeFalse
import org.junit.Before
import org.junit.Test
@@ -462,6 +461,36 @@
}
}
+ private fun ncWithAccessUids(vararg uids: Int) = NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_TEST)
+ .setAccessUids(uids.toSet()).build()
+
+ @Test
+ fun testRejectedUpdates() {
+ val callback = TestableNetworkCallback()
+ // will be cleaned up in tearDown
+ registerNetworkCallback(makeTestNetworkRequest(), callback)
+ val agent = createNetworkAgent(initialNc = ncWithAccessUids(200))
+ agent.register()
+ agent.markConnected()
+
+ // Make sure the UIDs have been ignored.
+ callback.expectCallback<Available>(agent.network!!)
+ callback.expectCapabilitiesThat(agent.network!!) {
+ it.accessUids.isEmpty() && !it.hasCapability(NET_CAPABILITY_VALIDATED)
+ }
+ callback.expectCallback<LinkPropertiesChanged>(agent.network!!)
+ callback.expectCallback<BlockedStatus>(agent.network!!)
+ callback.expectCapabilitiesThat(agent.network!!) {
+ it.accessUids.isEmpty() && it.hasCapability(NET_CAPABILITY_VALIDATED)
+ }
+ callback.assertNoCallback(NO_CALLBACK_TIMEOUT)
+
+ // Make sure that the UIDs are also ignored upon update
+ agent.sendNetworkCapabilities(ncWithAccessUids(200, 300))
+ callback.assertNoCallback(NO_CALLBACK_TIMEOUT)
+ }
+
@Test
fun testSendScore() {
// This test will create two networks and check that the one with the stronger
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 8f67e48..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() {
@@ -3813,14 +3819,14 @@
public void testNoMutableNetworkRequests() throws Exception {
final PendingIntent pendingIntent = PendingIntent.getBroadcast(
mContext, 0 /* requestCode */, new Intent("a"), FLAG_IMMUTABLE);
- NetworkRequest request1 = new NetworkRequest.Builder()
+ final NetworkRequest request1 = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_VALIDATED)
.build();
- NetworkRequest request2 = new NetworkRequest.Builder()
+ final NetworkRequest request2 = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL)
.build();
- Class<IllegalArgumentException> expected = IllegalArgumentException.class;
+ final Class<IllegalArgumentException> expected = IllegalArgumentException.class;
assertThrows(expected, () -> mCm.requestNetwork(request1, new NetworkCallback()));
assertThrows(expected, () -> mCm.requestNetwork(request1, pendingIntent));
assertThrows(expected, () -> mCm.requestNetwork(request2, new NetworkCallback()));
@@ -3828,6 +3834,36 @@
}
@Test
+ public void testNoAccessUidsInNetworkRequests() throws Exception {
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(
+ mContext, 0 /* requestCode */, new Intent("a"), FLAG_IMMUTABLE);
+ final NetworkRequest r = new NetworkRequest.Builder().build();
+ final ArraySet<Integer> accessUids = new ArraySet<>();
+ accessUids.add(6);
+ accessUids.add(9);
+ r.networkCapabilities.setAccessUids(accessUids);
+
+ final Handler handler = new Handler(ConnectivityThread.getInstanceLooper());
+ final NetworkCallback cb = new NetworkCallback();
+
+ final Class<IllegalArgumentException> expected = IllegalArgumentException.class;
+ assertThrows(expected, () -> mCm.requestNetwork(r, cb));
+ assertThrows(expected, () -> mCm.requestNetwork(r, pendingIntent));
+ assertThrows(expected, () -> mCm.registerNetworkCallback(r, cb));
+ assertThrows(expected, () -> mCm.registerNetworkCallback(r, cb, handler));
+ assertThrows(expected, () -> mCm.registerNetworkCallback(r, pendingIntent));
+ assertThrows(expected, () -> mCm.registerBestMatchingNetworkCallback(r, cb, handler));
+
+ // Make sure that resetting the access UIDs to the empty set will allow calling
+ // requestNetwork and registerNetworkCallback.
+ r.networkCapabilities.setAccessUids(Collections.emptySet());
+ mCm.requestNetwork(r, cb);
+ mCm.unregisterNetworkCallback(cb);
+ mCm.registerNetworkCallback(r, cb);
+ mCm.unregisterNetworkCallback(cb);
+ }
+
+ @Test
public void testMMSonWiFi() throws Exception {
// Test bringing up cellular without MMS NetworkRequest gets reaped
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
@@ -14571,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.
*/