Add accessUids to NetworkCapabilities.

For now, all entry points reject this. Followup changes
will allow the supported use cases.

Test: new unit tests and CTS for this in this patch
Change-Id: I7262811a2e46336d3bb63c80886fc0578a36da94
This commit is contained in:
Chalard Jean
2021-12-13 22:53:51 +09:00
parent 1522857564
commit 9a30acf744
6 changed files with 269 additions and 18 deletions

View File

@@ -264,6 +264,7 @@ public final class NetworkCapabilities implements Parcelable {
mTransportInfo = null;
mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED;
mUids = null;
mAccessUids.clear();
mAdministratorUids = new int[0];
mOwnerUid = Process.INVALID_UID;
mSSID = null;
@@ -294,6 +295,7 @@ public final class NetworkCapabilities implements Parcelable {
}
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 @@ public final class NetworkCapabilities implements Parcelable {
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 @@ public final class NetworkCapabilities implements Parcelable {
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.
@@ -1808,6 +1812,79 @@ public final class NetworkCapabilities implements Parcelable {
return false;
}
/**
* 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.
*
* Only network factories and the system server can see these UIDs, 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);
}
/**
* 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>
@@ -1962,6 +2039,7 @@ public final class NetworkCapabilities implements Parcelable {
&& equalsSpecifier(that)
&& equalsTransportInfo(that)
&& equalsUids(that)
&& equalsAccessUids(that)
&& equalsSSID(that)
&& equalsOwnerUid(that)
&& equalsPrivateDnsBroken(that)
@@ -1986,15 +2064,16 @@ public final class NetworkCapabilities implements Parcelable {
+ 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 +2101,7 @@ public final class NetworkCapabilities implements Parcelable {
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 +2114,7 @@ public final class NetworkCapabilities implements Parcelable {
}
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 +2128,11 @@ public final class NetworkCapabilities implements Parcelable {
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 +2209,11 @@ public final class NetworkCapabilities implements Parcelable {
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 +2390,7 @@ public final class NetworkCapabilities implements Parcelable {
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");
}
}
@@ -2908,6 +2998,44 @@ public final class NetworkCapabilities implements Parcelable {
return this;
}
/**
* 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>
* Only network factories and the system server can see these UIDs, 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.
*

View File

@@ -2099,6 +2099,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
newNc.setAdministratorUids(new int[0]);
if (!checkAnyPermissionOf(
callerPid, callerUid, android.Manifest.permission.NETWORK_FACTORY)) {
newNc.setAccessUids(new ArraySet<>());
newNc.setSubscriptionIds(Collections.emptySet());
}
@@ -6210,6 +6211,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
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.

View File

@@ -53,6 +53,7 @@ import android.os.RemoteException;
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;
@@ -1200,6 +1201,19 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
if (nc.hasTransport(TRANSPORT_TEST)) {
nc.restrictCapabilitiesForTestNetwork(creatorUid);
}
if (!areAccessUidsAcceptableFromNetworkAgent(nc)) {
nc.setAccessUids(new ArraySet<>());
}
}
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;
}
// TODO: Print shorter members first and only print the boolean variable which value is true

View File

@@ -308,6 +308,48 @@ public class NetworkCapabilitiesTest {
}
}
@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 @@ public class NetworkCapabilitiesTest {
.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);
}

View File

@@ -23,7 +23,6 @@ import android.net.INetworkAgent
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_VCN_MANAGED
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.RouteInfo
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.net.cts.NetworkAgentTest.TestableQosCallback.CallbackEntry.OnQosS
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.CompatUtil
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.OnUnregisterQosC
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 @@ class NetworkAgentTest {
}
}
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

View File

@@ -3813,20 +3813,50 @@ public class ConnectivityServiceTest {
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()));
assertThrows(expected, () -> mCm.requestNetwork(request2, pendingIntent));
}
@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