Add reserveNetwork API
reserveNetwork allows a requestor to initiate the creation of a network that requires the system to preallocate resources which can then be advertised by the requestor on or off device in order to help create the network. For example, this API will be used to reserve a IP over L2cap server network, which requires the system to first create a BluetoothServerSocket and reserve a PSM (i.e. port). This information is passed back to the requestor via onReserved() callback and subsequently advertised via a BLE advertisement. The network is established as soon as the client connects to the advertised PSM, which will trigger a call to onAvailable(), just like a regular NetworkRequest. onReserved() callbacks are exclusive to the reserveNetwork API and are sent when an NetworkOffer is registered that matches the reservation ID of an existing request. A reserved offer always follows the reservation request as it is created in direct response. onReserved() can only be called once. If for some reason the reservation gets cancelled by the system (i.e. the NetworkOffer is retracted) or cannot be fulfilled, onUnavailable() is called. It is up to the app to file a new NetworkRequest when this happens. An onReserved() callback is not queued, because a) it cannot be called more than once for the same reservation, b) it must always be the first callback, and c) it only gets cancelled by onUnavailable(). This change does not handle NetworkOffer migrations, such as updating the score, properly. This must be addressed before the feature can ship as the yieldToBadWifi logic can update the score on all NetworkOffers. API-Coverage-Bug: 372936361 Test: Very basic CSNetworkReservationTest; more to follow. Change-Id: I9b349d469830bf28e006cf403c605574371db267
This commit is contained in:
parent
4cef67c008
commit
73eee542a1
1 changed files with 175 additions and 32 deletions
|
|
@ -109,6 +109,7 @@ import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_5;
|
|||
import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION;
|
||||
import static android.net.NetworkCapabilities.REDACT_FOR_LOCAL_MAC_ADDRESS;
|
||||
import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
|
||||
import static android.net.NetworkCapabilities.RES_ID_UNSET;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
|
||||
|
|
@ -7801,6 +7802,28 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||
return mNetworkRequestForCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* NetworkCapabilities that were created as part of a NetworkOffer in response to a
|
||||
* RESERVATION request. mReservedCapabilities is null if no current offer matches the
|
||||
* RESERVATION request or if the request is not a RESERVATION. Matching is based on
|
||||
* reservationId.
|
||||
*/
|
||||
@Nullable
|
||||
private NetworkCapabilities mReservedCapabilities;
|
||||
@Nullable
|
||||
NetworkCapabilities getReservedCapabilities() {
|
||||
return mReservedCapabilities;
|
||||
}
|
||||
|
||||
void setReservedCapabilities(@NonNull NetworkCapabilities caps) {
|
||||
// This function can only be called once. NetworkCapabilities are never reset as the
|
||||
// reservation is released when the offer disappears.
|
||||
if (mReservedCapabilities != null) {
|
||||
logwtf("ReservedCapabilities can only be set once");
|
||||
}
|
||||
mReservedCapabilities = caps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of UIDs this nri applies to.
|
||||
*/
|
||||
|
|
@ -8161,6 +8184,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||
return PREFERENCE_ORDER_NONE;
|
||||
}
|
||||
|
||||
public int getReservationId() {
|
||||
// RESERVATIONs cannot be used in multilayer requests.
|
||||
if (isMultilayerRequest()) return RES_ID_UNSET;
|
||||
final NetworkRequest req = mRequests.get(0);
|
||||
// Non-reservation types return RES_ID_UNSET.
|
||||
return req.networkCapabilities.getReservationId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
// As an immutable collection, mRequests cannot change by the time the
|
||||
|
|
@ -9381,6 +9412,18 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private NetworkRequestInfo maybeGetNriForReservedOffer(NetworkOfferInfo noi) {
|
||||
final int reservationId = noi.offer.caps.getReservationId();
|
||||
if (reservationId == RES_ID_UNSET) return null; // not a reserved offer.
|
||||
|
||||
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
|
||||
if (reservationId == nri.getReservationId()) return nri;
|
||||
}
|
||||
// The reservation was withdrawn or the reserving process died.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register or update a network offer.
|
||||
* @param newOffer The new offer. If the callback member is the same as an existing
|
||||
|
|
@ -9398,6 +9441,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||
}
|
||||
final NetworkOfferInfo existingOffer = findNetworkOfferInfoByCallback(newOffer.callback);
|
||||
if (null != existingOffer) {
|
||||
// TODO: to support updating the score for reserved offers by calling
|
||||
// ConnectivityManager#offerNetwork with the same callback object or via
|
||||
// updateOfferScore, prevent handleUnregisterNetworkOffer() from sending an
|
||||
// onUnavailable() callback here.
|
||||
handleUnregisterNetworkOffer(existingOffer);
|
||||
newOffer.migrateFrom(existingOffer.offer);
|
||||
if (DBG) {
|
||||
|
|
@ -9410,6 +9457,25 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||
}
|
||||
}
|
||||
final NetworkOfferInfo noi = new NetworkOfferInfo(newOffer);
|
||||
final NetworkRequestInfo reservationNri = maybeGetNriForReservedOffer(noi);
|
||||
if (reservationNri != null) {
|
||||
// A NetworkRequest is only allowed to trigger a single reserved offer (and onReserved()
|
||||
// callback). All subsequent offers are ignored. This either indicates a bug in the
|
||||
// provider (e.g., responding twice to the same reservation, or updating the
|
||||
// capabilities of a reserved offer), or multiple providers responding to the same offer
|
||||
// (which could happen, but is not useful to the requesting app).
|
||||
// TODO: add proper support for offer migration; i.e. allow the score of a reservation
|
||||
// offer to be updated.
|
||||
if (reservationNri.getReservedCapabilities() != null) {
|
||||
loge("A reservation can only trigger a single offer; new offer is ignored.");
|
||||
return;
|
||||
}
|
||||
// Always update the reserved offer before calling callCallbackForRequest.
|
||||
reservationNri.setReservedCapabilities(noi.offer.caps);
|
||||
callCallbackForRequest(
|
||||
reservationNri, null /* networkAgent */, CALLBACK_RESERVED, 0 /* arg1 */);
|
||||
}
|
||||
|
||||
try {
|
||||
noi.offer.callback.asBinder().linkToDeath(noi, 0 /* flags */);
|
||||
} catch (RemoteException e) {
|
||||
|
|
@ -9430,6 +9496,19 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||
// function may be called twice in a row, but the array will no longer contain
|
||||
// the offer.
|
||||
if (!mNetworkOffers.remove(noi)) return;
|
||||
|
||||
// If the offer was brought up as a result of a reservation, inform the RESERVATION request
|
||||
// that it has disappeared. There is no need to reset nri.mReservedCapabilities to null, as
|
||||
// CALLBACK_UNAVAIL will cause the request to be torn down. In addition, leaving
|
||||
// nri.mReservedOffer set prevents an additional onReserved() callback in
|
||||
// handleRegisterNetworkOffer() in the case of a migration (which would be ignored as it
|
||||
// follows an onUnavailable).
|
||||
final NetworkRequestInfo nri = maybeGetNriForReservedOffer(noi);
|
||||
if (nri != null) {
|
||||
handleRemoveNetworkRequest(nri);
|
||||
callCallbackForRequest(nri, null /* networkAgent */, CALLBACK_UNAVAIL, 0 /* arg1 */);
|
||||
}
|
||||
|
||||
noi.offer.callback.asBinder().unlinkToDeath(noi, 0 /* flags */);
|
||||
}
|
||||
|
||||
|
|
@ -10648,9 +10727,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||
return bundle;
|
||||
}
|
||||
|
||||
// networkAgent is only allowed to be null if notificationType is
|
||||
// CALLBACK_UNAVAIL. This is because UNAVAIL is about no network being
|
||||
// available, while all other cases are about some particular network.
|
||||
// networkAgent is only allowed to be null if notificationType is CALLBACK_UNAVAIL or
|
||||
// CALLBACK_RESERVED. This is because, per definition, no network is available for UNAVAIL, and
|
||||
// RESERVED callbacks happen when a NetworkOffer is created in response to a reservation.
|
||||
private void callCallbackForRequest(@NonNull final NetworkRequestInfo nri,
|
||||
@Nullable final NetworkAgentInfo networkAgent, final int notificationType,
|
||||
final int arg1) {
|
||||
|
|
@ -10662,6 +10741,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||
}
|
||||
// Even if a callback ends up not being sent, it may affect other callbacks in the queue, so
|
||||
// queue callbacks before checking the declared methods flags.
|
||||
// UNAVAIL and RESERVED callbacks are safe not to be queued, because RESERVED must always be
|
||||
// the first callback. In addition, RESERVED cannot be sent more than once and is only
|
||||
// cancelled by UNVAIL.
|
||||
// TODO: evaluate whether it makes sense to queue RESERVED callbacks.
|
||||
if (networkAgent != null && nri.maybeQueueCallback(networkAgent, notificationType)) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -10669,14 +10752,24 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||
// No need to send the notification as the recipient method is not overridden
|
||||
return;
|
||||
}
|
||||
final Network bundleNetwork = notificationType == CALLBACK_UNAVAIL
|
||||
? null
|
||||
: networkAgent.network;
|
||||
// networkAgent is only null for UNAVAIL and RESERVED.
|
||||
final Network bundleNetwork = (networkAgent != null) ? networkAgent.network : null;
|
||||
final Bundle bundle = makeCommonBundleForCallback(nri, bundleNetwork);
|
||||
final boolean includeLocationSensitiveInfo =
|
||||
(nri.mCallbackFlags & NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) != 0;
|
||||
final NetworkRequest nrForCallback = nri.getNetworkRequestForCallback();
|
||||
switch (notificationType) {
|
||||
case CALLBACK_RESERVED: {
|
||||
final NetworkCapabilities nc =
|
||||
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
|
||||
networkCapabilitiesRestrictedForCallerPermissions(
|
||||
nri.getReservedCapabilities(), nri.mPid, nri.mUid),
|
||||
includeLocationSensitiveInfo, nri.mPid, nri.mUid,
|
||||
nrForCallback.getRequestorPackageName(),
|
||||
nri.mCallingAttributionTag);
|
||||
putParcelable(bundle, nc);
|
||||
break;
|
||||
}
|
||||
case CALLBACK_AVAILABLE: {
|
||||
final NetworkCapabilities nc =
|
||||
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
|
||||
|
|
|
|||
Loading…
Reference in a new issue