Merge changes Icecb889a,Id06116ad into main
* changes:
Set the reservationId when a new NetworkRequest is created
Add reservationId to NetworkCapabilities
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index 4a50397..c6b62ee 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -359,6 +359,7 @@
mSubIds = new ArraySet<>();
mUnderlyingNetworks = null;
mEnterpriseId = 0;
+ mReservationId = RES_ID_UNSET;
}
/**
@@ -393,6 +394,7 @@
// necessary.
mUnderlyingNetworks = nc.mUnderlyingNetworks;
mEnterpriseId = nc.mEnterpriseId;
+ mReservationId = nc.mReservationId;
}
/**
@@ -2233,7 +2235,8 @@
&& (onlyImmutable || satisfiedByUids(nc))
&& (onlyImmutable || satisfiedBySSID(nc))
&& (onlyImmutable || satisfiedByRequestor(nc))
- && (onlyImmutable || satisfiedBySubscriptionIds(nc)));
+ && (onlyImmutable || satisfiedBySubscriptionIds(nc)))
+ && satisfiedByReservationId(nc);
}
/**
@@ -2347,7 +2350,8 @@
&& equalsAdministratorUids(that)
&& equalsSubscriptionIds(that)
&& equalsUnderlyingNetworks(that)
- && equalsEnterpriseCapabilitiesId(that);
+ && equalsEnterpriseCapabilitiesId(that)
+ && equalsReservationId(that);
}
@Override
@@ -2373,7 +2377,9 @@
+ Arrays.hashCode(mAdministratorUids) * 67
+ Objects.hashCode(mSubIds) * 71
+ Objects.hashCode(mUnderlyingNetworks) * 73
- + mEnterpriseId * 79;
+ + mEnterpriseId * 79
+ + mReservationId * 83;
+
}
@Override
@@ -2411,6 +2417,7 @@
dest.writeIntArray(CollectionUtils.toIntArray(mSubIds));
dest.writeTypedList(mUnderlyingNetworks);
dest.writeInt(mEnterpriseId & ALL_VALID_ENTERPRISE_IDS);
+ dest.writeInt(mReservationId);
}
public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
@@ -2446,6 +2453,7 @@
}
netCap.setUnderlyingNetworks(in.createTypedArrayList(Network.CREATOR));
netCap.mEnterpriseId = in.readInt() & ALL_VALID_ENTERPRISE_IDS;
+ netCap.mReservationId = in.readInt();
return netCap;
}
@Override
@@ -2548,6 +2556,11 @@
NetworkCapabilities::enterpriseIdNameOf, "&");
}
+ if (mReservationId != RES_ID_UNSET) {
+ final boolean isReservationOffer = (mReservationId == RES_ID_MATCH_ALL_RESERVATIONS);
+ sb.append(" ReservationId: ").append(isReservationOffer ? "*" : mReservationId);
+ }
+
sb.append(" UnderlyingNetworks: ");
if (mUnderlyingNetworks != null) {
sb.append("[");
@@ -2876,6 +2889,65 @@
}
/**
+ * The reservation ID used by non-reservable Networks and "regular" NetworkOffers.
+ *
+ * Note that {@code NetworkRequest#FIRST_REQUEST_ID} is 1;
+ * @hide
+ */
+ public static final int RES_ID_UNSET = 0;
+
+ /**
+ * The reservation ID used by special NetworkOffers that handle RESERVATION requests.
+ *
+ * NetworkOffers with {@code RES_ID_MATCH_ALL_RESERVATIONS} *only* receive onNetworkNeeded()
+ * callbacks for {@code NetworkRequest.Type.RESERVATION}.
+ * @hide
+ */
+ public static final int RES_ID_MATCH_ALL_RESERVATIONS = -1;
+
+ /**
+ * Unique ID that identifies the network reservation.
+ */
+ private int mReservationId;
+
+ /**
+ * Returns the reservation ID
+ * @hide
+ */
+ public int getReservationId() {
+ return mReservationId;
+ }
+
+ /**
+ * Set the reservation ID
+ * @hide
+ */
+ public void setReservationId(int resId) {
+ mReservationId = resId;
+ }
+
+ private boolean equalsReservationId(@NonNull NetworkCapabilities nc) {
+ return mReservationId == nc.mReservationId;
+ }
+
+ private boolean satisfiedByReservationId(@NonNull NetworkCapabilities nc) {
+ if (mReservationId == RES_ID_UNSET) {
+ // To maintain regular NetworkRequest semantics, a request with a zero reservationId
+ // matches an offer or network with any reservationId except MATCH_ALL_RESERVATIONS.
+ return nc.mReservationId != RES_ID_MATCH_ALL_RESERVATIONS;
+ }
+ // A request with a non-zero reservationId matches only an offer or network with that exact
+ // reservationId (required to match the network that will eventually come up) or
+ // MATCH_ALL_RESERVATIONS (required to match the blanket reservation offer).
+ if (nc.mReservationId == RES_ID_MATCH_ALL_RESERVATIONS) {
+ return true;
+ }
+ return mReservationId == nc.mReservationId;
+ }
+
+
+
+ /**
* Returns a bitmask of all the applicable redactions (based on the permissions held by the
* receiving app) to be performed on this object.
*
diff --git a/framework/src/android/net/NetworkRequest.java b/framework/src/android/net/NetworkRequest.java
index 4cc3c61..5ae25ab 100644
--- a/framework/src/android/net/NetworkRequest.java
+++ b/framework/src/android/net/NetworkRequest.java
@@ -258,6 +258,12 @@
}
requestId = rId;
networkCapabilities = nc;
+ if (type == Type.RESERVATION) {
+ // Conceptually, the reservationId is not related to the requestId; however, the
+ // requestId fulfills the same uniqueness requirements that are needed for the
+ // reservationId, so it can be reused for this purpose.
+ networkCapabilities.setReservationId(rId);
+ }
this.legacyType = legacyType;
this.type = type;
}
diff --git a/tests/common/java/android/net/NetworkCapabilitiesTest.java b/tests/common/java/android/net/NetworkCapabilitiesTest.java
index 0f0e2f1..d694637 100644
--- a/tests/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/common/java/android/net/NetworkCapabilitiesTest.java
@@ -382,6 +382,7 @@
netCap.setAllowedUids(allowedUids);
netCap.setSubscriptionIds(Set.of(TEST_SUBID1, TEST_SUBID2));
netCap.setUids(uids);
+ netCap.setReservationId(42);
}
netCap.setOwnerUid(123);
@@ -1493,4 +1494,42 @@
// nc1 and nc2 are the same since invalid capability is ignored
assertEquals(nc1, nc2);
}
+
+ @Test
+ public void testReservationIdMatching() {
+ final NetworkCapabilities requestNc = new NetworkCapabilities();
+ final NetworkCapabilities reservationNc = new NetworkCapabilities();
+ reservationNc.setReservationId(42);
+
+ final NetworkCapabilities reservedNetworkNc = new NetworkCapabilities(reservationNc);
+ final NetworkCapabilities otherNetworkNc = new NetworkCapabilities();
+ final NetworkCapabilities otherReservedNetworkNc = new NetworkCapabilities();
+ otherReservedNetworkNc.setReservationId(99);
+ final NetworkCapabilities offerNc = new NetworkCapabilities();
+ offerNc.setReservationId(NetworkCapabilities.RES_ID_MATCH_ALL_RESERVATIONS);
+
+ // A regular request can match any network or offer except one with MATCH_ALL_RESERVATIONS
+ assertTrue(requestNc.satisfiedByNetworkCapabilities(reservedNetworkNc));
+ assertTrue(requestNc.satisfiedByNetworkCapabilities(otherNetworkNc));
+ assertTrue(requestNc.satisfiedByNetworkCapabilities(otherReservedNetworkNc));
+ assertFalse(requestNc.satisfiedByNetworkCapabilities(offerNc));
+
+ // A reservation request can only match the reservedNetwork and the blanket offer.
+ assertTrue(reservationNc.satisfiedByNetworkCapabilities(reservedNetworkNc));
+ assertFalse(reservationNc.satisfiedByNetworkCapabilities(otherNetworkNc));
+ assertFalse(reservationNc.satisfiedByNetworkCapabilities(otherReservedNetworkNc));
+ assertTrue(reservationNc.satisfiedByNetworkCapabilities(offerNc));
+ }
+
+ @Test @IgnoreUpTo(SC_V2)
+ public void testReservationIdEquals() {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ nc.setReservationId(42);
+ final NetworkCapabilities other = new NetworkCapabilities(nc);
+
+ assertEquals(nc, other);
+
+ nc.setReservationId(43);
+ assertNotEquals(nc, other);
+ }
}
diff --git a/tests/cts/net/src/android/net/cts/NetworkRequestTest.java b/tests/cts/net/src/android/net/cts/NetworkRequestTest.java
index cdca8dc..2226f4c 100644
--- a/tests/cts/net/src/android/net/cts/NetworkRequestTest.java
+++ b/tests/cts/net/src/android/net/cts/NetworkRequestTest.java
@@ -16,6 +16,7 @@
package android.net.cts;
+import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_FOTA;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
@@ -554,4 +555,32 @@
.setBssidPattern(ARBITRARY_ADDRESS, ARBITRARY_ADDRESS)
.build();
}
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testNetworkReservation() {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ final NetworkCapabilities blanketOffer = new NetworkCapabilities(nc);
+ blanketOffer.setReservationId(NetworkCapabilities.RES_ID_MATCH_ALL_RESERVATIONS);
+ final NetworkCapabilities specificOffer = new NetworkCapabilities(nc);
+ specificOffer.setReservationId(42);
+ final NetworkCapabilities otherSpecificOffer = new NetworkCapabilities(nc);
+ otherSpecificOffer.setReservationId(43);
+ final NetworkCapabilities regularOffer = new NetworkCapabilities(nc);
+
+ final NetworkRequest reservationNR = new NetworkRequest(new NetworkCapabilities(nc),
+ TYPE_NONE, 42 /* rId */, NetworkRequest.Type.RESERVATION);
+ final NetworkRequest requestNR = new NetworkRequest(new NetworkCapabilities(nc),
+ TYPE_NONE, 42 /* rId */, NetworkRequest.Type.REQUEST);
+
+ assertTrue(reservationNR.canBeSatisfiedBy(blanketOffer));
+ assertTrue(reservationNR.canBeSatisfiedBy(specificOffer));
+ assertFalse(reservationNR.canBeSatisfiedBy(otherSpecificOffer));
+ assertFalse(reservationNR.canBeSatisfiedBy(regularOffer));
+
+ assertFalse(requestNR.canBeSatisfiedBy(blanketOffer));
+ assertTrue(requestNR.canBeSatisfiedBy(specificOffer));
+ assertTrue(requestNR.canBeSatisfiedBy(otherSpecificOffer));
+ assertTrue(requestNR.canBeSatisfiedBy(regularOffer));
+ }
}
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSNetworkReservationTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSNetworkReservationTest.kt
index db3bbb7..a159697 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSNetworkReservationTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSNetworkReservationTest.kt
@@ -22,6 +22,7 @@
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
+import android.net.NetworkCapabilities.RES_ID_MATCH_ALL_RESERVATIONS
import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
import android.net.NetworkProvider
import android.net.NetworkRequest
@@ -60,22 +61,25 @@
context.packageName, context.attributionTag, NetworkCallback.DECLARED_METHODS_ALL)
}
+ fun NetworkCapabilities.copyWithReservationId(resId: Int) = NetworkCapabilities(this).also {
+ it.reservationId = resId
+ }
+
@Test
fun testReservationTriggersOnNetworkNeeded() {
val provider = NetworkProvider(context, csHandlerThread.looper, "Ethernet provider")
- val offerCb = TestableNetworkOfferCallback(TIMEOUT_MS, NO_CB_TIMEOUT_MS)
+ val blanketOfferCb = TestableNetworkOfferCallback(TIMEOUT_MS, NO_CB_TIMEOUT_MS)
cm.registerNetworkProvider(provider)
- provider.registerNetworkOffer(ETHERNET_SCORE, ETHERNET_CAPS, {r -> r.run()}, offerCb)
- // TODO: add reservationId to offer, so it doesn't match the default request.
- offerCb.expectOnNetworkNeeded(ETHERNET_CAPS)
+ val blanketCaps = ETHERNET_CAPS.copyWithReservationId(RES_ID_MATCH_ALL_RESERVATIONS)
+ provider.registerNetworkOffer(ETHERNET_SCORE, blanketCaps, {r -> r.run()}, blanketOfferCb)
val req = NetworkRequest.Builder().addTransportType(TRANSPORT_ETHERNET).build()
val cb = NetworkCallback()
cm.reserveNetwork(req, cb)
- offerCb.expectOnNetworkNeeded(req.networkCapabilities)
+ blanketOfferCb.expectOnNetworkNeeded(blanketCaps)
// TODO: also test onNetworkUnneeded is called once ConnectivityManager supports the
// reserveNetwork API.