[NS05] Feed network offer callbacks
The design is very simply expressed :
An offer is needed for a request if and only if that offer
might beat the satisfier for that request.
The implementation of "might beat" is NetworkRanker#mightBeat.
Test: FrameworksNetTests FrameworksWifiTests NetworkStackTests
Bug: 167544279
Merged-In: I3c2563d4ae4e3715d0c6270344ba8f7ef067872f
Merged-In: I0fe911eef2483ecbac48c733d56283b81538690a
Change-Id: I0fe911eef2483ecbac48c733d56283b81538690a
(cherry-picked from 7b6a33bd504e88ac051db9511eafe915d80e7539)
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 3771bbc..f7d8aa0 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -3359,8 +3359,6 @@
nai.lastValidated = valid;
nai.everValidated |= valid;
updateCapabilities(oldScore, nai, nai.networkCapabilities);
- // If score has changed, rebroadcast to NetworkProviders. b/17726566
- if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
if (valid) {
handleFreshlyValidatedNetwork(nai);
// Clear NO_INTERNET, PRIVATE_DNS_BROKEN, PARTIAL_CONNECTIVITY and
@@ -3781,9 +3779,12 @@
if (currentNetwork != null
&& currentNetwork.network.getNetId() == nai.network.getNetId()) {
// uid rules for this network will be removed in destroyNativeNetwork(nai).
+ // TODO : setting the satisfier is in fact the job of the rematch. Teach the
+ // rematch not to keep disconnected agents instead of setting it here ; this
+ // will also allow removing updating the offers below.
nri.setSatisfier(null, null);
- if (request.isRequest()) {
- sendUpdatedScoreToFactories(request, null);
+ for (final NetworkOfferInfo noi : mNetworkOffers) {
+ informOffer(nri, noi.offer, mNetworkRanker);
}
if (mDefaultRequest == nri) {
@@ -3939,16 +3940,13 @@
}
rematchAllNetworksAndRequests();
- for (final NetworkRequestInfo nri : nris) {
- // If the nri is satisfied, return as its score has already been sent if needed.
- if (nri.isBeingSatisfied()) {
- return;
- }
- // As this request was not satisfied on rematch and thus never had any scores sent to
- // the factories, send null now for each request of type REQUEST.
- for (final NetworkRequest req : nri.mRequests) {
- if (req.isRequest()) sendUpdatedScoreToFactories(req, null);
+ // Requests that have not been matched to a network will not have been sent to the
+ // providers, because the old satisfier and the new satisfier are the same (null in this
+ // case). Send these requests to the providers.
+ for (final NetworkRequestInfo nri : nris) {
+ for (final NetworkOfferInfo noi : mNetworkOffers) {
+ informOffer(nri, noi.offer, mNetworkRanker);
}
}
}
@@ -4156,7 +4154,15 @@
}
}
- cancelNpiRequests(nri);
+ // For all outstanding offers, cancel any of the layers of this NRI that used to be
+ // needed for this offer.
+ for (final NetworkOfferInfo noi : mNetworkOffers) {
+ for (final NetworkRequest req : nri.mRequests) {
+ if (req.isRequest() && noi.offer.neededFor(req)) {
+ noi.offer.onNetworkUnneeded(req);
+ }
+ }
+ }
}
private void handleRemoveNetworkRequests(@NonNull final Set<NetworkRequestInfo> nris) {
@@ -4169,20 +4175,6 @@
}
}
- private void cancelNpiRequests(@NonNull final NetworkRequestInfo nri) {
- for (final NetworkRequest req : nri.mRequests) {
- cancelNpiRequest(req);
- }
- }
-
- private void cancelNpiRequest(@NonNull final NetworkRequest req) {
- if (req.isRequest()) {
- for (final NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
- npi.cancelRequest(req);
- }
- }
- }
-
private void removeListenRequestFromNetworks(@NonNull final NetworkRequest req) {
// listens don't have a singular affected Network. Check all networks to see
// if this listen request applies and remove it.
@@ -4309,7 +4301,6 @@
nai.networkAgentConfig.acceptPartialConnectivity = accept;
nai.updateScoreForNetworkAgentConfigUpdate();
rematchAllNetworksAndRequests();
- sendUpdatedScoreToFactories(nai);
}
if (always) {
@@ -4377,7 +4368,6 @@
if (!nai.avoidUnvalidated) {
nai.avoidUnvalidated = true;
rematchAllNetworksAndRequests();
- sendUpdatedScoreToFactories(nai);
}
}
@@ -4482,14 +4472,9 @@
return avoidBadWifi();
}
-
+ // TODO : this function is now useless.
private void rematchForAvoidBadWifiUpdate() {
rematchAllNetworksAndRequests();
- for (NetworkAgentInfo nai: mNetworkAgentInfos) {
- if (nai.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
- sendUpdatedScoreToFactories(nai);
- }
- }
}
// TODO: Evaluate whether this is of interest to other consumers of
@@ -5514,24 +5499,6 @@
}
}
- void sendMessageToNetworkProvider(int what, int arg1, int arg2, Object obj) {
- try {
- messenger.send(Message.obtain(null /* handler */, what, arg1, arg2, obj));
- } catch (RemoteException e) {
- // Remote process died. Ignore; the death recipient will remove this
- // NetworkProviderInfo from mNetworkProviderInfos.
- }
- }
-
- void requestNetwork(NetworkRequest request, int score, int servingProviderId) {
- sendMessageToNetworkProvider(NetworkProvider.CMD_REQUEST_NETWORK, score,
- servingProviderId, request);
- }
-
- void cancelRequest(NetworkRequest request) {
- sendMessageToNetworkProvider(NetworkProvider.CMD_CANCEL_REQUEST, 0, 0, request);
- }
-
void connect(Context context, Handler handler) {
try {
messenger.getBinder().linkToDeath(mDeathRecipient, 0);
@@ -6240,7 +6207,6 @@
if (DBG) log("Got NetworkProvider Messenger for " + npi.name);
mNetworkProviderInfos.put(npi.messenger, npi);
npi.connect(mContext, mTrackerHandler);
- sendAllRequestsToProvider(npi);
}
@Override
@@ -6263,6 +6229,9 @@
public void offerNetwork(final int providerId,
@NonNull final NetworkScore score, @NonNull final NetworkCapabilities caps,
@NonNull final INetworkOfferCallback callback) {
+ Objects.requireNonNull(score);
+ Objects.requireNonNull(caps);
+ Objects.requireNonNull(callback);
final NetworkOffer offer = new NetworkOffer(
FullScore.makeProspectiveScore(score, caps), caps, callback, providerId);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_OFFER, offer));
@@ -6287,7 +6256,7 @@
toRemove.add(noi);
}
}
- for (NetworkOfferInfo noi : toRemove) {
+ for (final NetworkOfferInfo noi : toRemove) {
handleUnregisterNetworkOffer(noi);
}
if (DBG) log("unregisterNetworkProvider for " + npi.name);
@@ -6707,7 +6676,7 @@
return;
}
mNetworkOffers.add(noi);
- // TODO : send requests to the provider.
+ issueNetworkNeeds(noi);
}
private void handleUnregisterNetworkOffer(@NonNull final NetworkOfferInfo noi) {
@@ -7482,100 +7451,6 @@
updateLinkProperties(nai, newLp, new LinkProperties(nai.linkProperties));
}
- private void sendUpdatedScoreToFactories(NetworkAgentInfo nai) {
- for (int i = 0; i < nai.numNetworkRequests(); i++) {
- NetworkRequest nr = nai.requestAt(i);
- // Don't send listening or track default request to factories. b/17393458
- if (!nr.isRequest()) continue;
- sendUpdatedScoreToFactories(nr, nai);
- }
- }
-
- private void sendUpdatedScoreToFactories(
- @NonNull final NetworkReassignment.RequestReassignment event) {
- // If a request of type REQUEST is now being satisfied by a new network.
- if (null != event.mNewNetworkRequest && event.mNewNetworkRequest.isRequest()) {
- sendUpdatedScoreToFactories(event.mNewNetworkRequest, event.mNewNetwork);
- }
-
- // If a previously satisfied request of type REQUEST is no longer being satisfied.
- if (null != event.mOldNetworkRequest && event.mOldNetworkRequest.isRequest()
- && event.mOldNetworkRequest != event.mNewNetworkRequest) {
- sendUpdatedScoreToFactories(event.mOldNetworkRequest, null);
- }
-
- cancelMultilayerLowerPriorityNpiRequests(event.mNetworkRequestInfo);
- }
-
- /**
- * Cancel with all NPIs the given NRI's multilayer requests that are a lower priority than
- * its currently satisfied active request.
- * @param nri the NRI to cancel lower priority requests for.
- */
- private void cancelMultilayerLowerPriorityNpiRequests(
- @NonNull final NetworkRequestInfo nri) {
- if (!nri.isMultilayerRequest() || null == nri.mActiveRequest) {
- return;
- }
-
- final int indexOfNewRequest = nri.mRequests.indexOf(nri.mActiveRequest);
- for (int i = indexOfNewRequest + 1; i < nri.mRequests.size(); i++) {
- cancelNpiRequest(nri.mRequests.get(i));
- }
- }
-
- private void sendUpdatedScoreToFactories(@NonNull NetworkRequest networkRequest,
- @Nullable NetworkAgentInfo nai) {
- final int score;
- final int serial;
- if (nai != null) {
- score = nai.getCurrentScore();
- serial = nai.factorySerialNumber;
- } else {
- score = 0;
- serial = 0;
- }
- if (VDBG || DDBG){
- log("sending new Min Network Score(" + score + "): " + networkRequest.toString());
- }
- for (NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
- npi.requestNetwork(networkRequest, score, serial);
- }
- }
-
- /** Sends all current NetworkRequests to the specified factory. */
- private void sendAllRequestsToProvider(@NonNull final NetworkProviderInfo npi) {
- ensureRunningOnConnectivityServiceThread();
- for (final NetworkRequestInfo nri : getNrisFromGlobalRequests()) {
- for (final NetworkRequest req : nri.mRequests) {
- if (!req.isRequest() && nri.getActiveRequest() == req) {
- break;
- }
- if (!req.isRequest()) {
- continue;
- }
- // Only set the nai for the request it is satisfying.
- final NetworkAgentInfo nai =
- nri.getActiveRequest() == req ? nri.getSatisfier() : null;
- final int score;
- final int serial;
- if (null != nai) {
- score = nai.getCurrentScore();
- serial = nai.factorySerialNumber;
- } else {
- score = 0;
- serial = NetworkProvider.ID_NONE;
- }
- npi.requestNetwork(req, score, serial);
- // For multilayer requests, don't send lower priority requests if a higher priority
- // request is already satisfied.
- if (null != nai) {
- break;
- }
- }
- }
- }
-
private void sendPendingIntentForRequest(NetworkRequestInfo nri, NetworkAgentInfo networkAgent,
int notificationType) {
if (notificationType == ConnectivityManager.CALLBACK_AVAILABLE && !nri.mPendingIntentSent) {
@@ -8067,6 +7942,7 @@
log(changes.toString()); // Shorter form, only one line of log
}
applyNetworkReassignment(changes, now);
+ issueNetworkNeeds();
}
private void applyNetworkReassignment(@NonNull final NetworkReassignment changes,
@@ -8098,12 +7974,6 @@
// before LegacyTypeTracker sends legacy broadcasts
for (final NetworkReassignment.RequestReassignment event :
changes.getRequestReassignments()) {
- // Tell NetworkProviders about the new score, so they can stop
- // trying to connect if they know they cannot match it.
- // TODO - this could get expensive if there are a lot of outstanding requests for this
- // network. Think of a way to reduce this. Push netid->request mapping to each factory?
- sendUpdatedScoreToFactories(event);
-
if (null != event.mNewNetwork) {
notifyNetworkAvailable(event.mNewNetwork, event.mNetworkRequestInfo);
} else {
@@ -8240,6 +8110,107 @@
}
}
+ private void issueNetworkNeeds() {
+ ensureRunningOnConnectivityServiceThread();
+ for (final NetworkOfferInfo noi : mNetworkOffers) {
+ issueNetworkNeeds(noi);
+ }
+ }
+
+ private void issueNetworkNeeds(@NonNull final NetworkOfferInfo noi) {
+ ensureRunningOnConnectivityServiceThread();
+ for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
+ informOffer(nri, noi.offer, mNetworkRanker);
+ }
+ }
+
+ /**
+ * Inform a NetworkOffer about any new situation of a request.
+ *
+ * This function handles updates to offers. A number of events may happen that require
+ * updating the registrant for this offer about the situation :
+ * • The offer itself was updated. This may lead the offer to no longer being able
+ * to satisfy a request or beat a satisfier (and therefore be no longer needed),
+ * or conversely being strengthened enough to beat the satisfier (and therefore
+ * start being needed)
+ * • The network satisfying a request changed (including cases where the request
+ * starts or stops being satisfied). The new network may be a stronger or weaker
+ * match than the old one, possibly affecting whether the offer is needed.
+ * • The network satisfying a request updated their score. This may lead the offer
+ * to no longer be able to beat it if the current satisfier got better, or
+ * conversely start being a good choice if the current satisfier got weaker.
+ *
+ * @param nri The request
+ * @param offer The offer. This may be an updated offer.
+ */
+ private static void informOffer(@NonNull NetworkRequestInfo nri,
+ @NonNull final NetworkOffer offer, @NonNull final NetworkRanker networkRanker) {
+ final NetworkRequest activeRequest = nri.isBeingSatisfied() ? nri.getActiveRequest() : null;
+ final NetworkAgentInfo satisfier = null != activeRequest ? nri.getSatisfier() : null;
+ final FullScore satisfierScore = null != satisfier ? satisfier.getScore() : null;
+
+ // Multi-layer requests have a currently active request, the one being satisfied.
+ // Since the system will try to bring up a better network than is currently satisfying
+ // the request, NetworkProviders need to be told the offers matching the requests *above*
+ // the currently satisfied one are needed, that the ones *below* the satisfied one are
+ // not needed, and the offer is needed for the active request iff the offer can beat
+ // the satisfier.
+ // For non-multilayer requests, the logic above gracefully degenerates to only the
+ // last case.
+ // To achieve this, the loop below will proceed in three steps. In a first phase, inform
+ // providers that the offer is needed for this request, until the active request is found.
+ // In a second phase, deal with the currently active request. In a third phase, inform
+ // the providers that offer is unneeded for the remaining requests.
+
+ // First phase : inform providers of all requests above the active request.
+ int i;
+ for (i = 0; nri.mRequests.size() > i; ++i) {
+ final NetworkRequest request = nri.mRequests.get(i);
+ if (activeRequest == request) break; // Found the active request : go to phase 2
+ if (!request.isRequest()) continue; // Listens/track defaults are never sent to offers
+ // Since this request is higher-priority than the one currently satisfied, if the
+ // offer can satisfy it, the provider should try and bring up the network for sure ;
+ // no need to even ask the ranker – an offer that can satisfy is always better than
+ // no network. Hence tell the provider so unless it already knew.
+ if (request.canBeSatisfiedBy(offer.caps) && !offer.neededFor(request)) {
+ offer.onNetworkNeeded(request);
+ }
+ }
+
+ // Second phase : deal with the active request (if any)
+ if (null != activeRequest && activeRequest.isRequest()) {
+ final boolean oldNeeded = offer.neededFor(activeRequest);
+ // An offer is needed if it is currently served by this provider or if this offer
+ // can beat the current satisfier.
+ final boolean currentlyServing = satisfier != null
+ && satisfier.factorySerialNumber == offer.providerId;
+ final boolean newNeeded = (currentlyServing
+ || (activeRequest.canBeSatisfiedBy(offer.caps)
+ && networkRanker.mightBeat(activeRequest, satisfierScore, offer)));
+ if (newNeeded != oldNeeded) {
+ if (newNeeded) {
+ offer.onNetworkNeeded(activeRequest);
+ } else {
+ // The offer used to be able to beat the satisfier. Now it can't.
+ offer.onNetworkUnneeded(activeRequest);
+ }
+ }
+ }
+
+ // Third phase : inform the providers that the offer isn't needed for any request
+ // below the active one.
+ for (++i /* skip the active request */; nri.mRequests.size() > i; ++i) {
+ final NetworkRequest request = nri.mRequests.get(i);
+ if (!request.isRequest()) continue; // Listens/track defaults are never sent to offers
+ // Since this request is lower-priority than the one currently satisfied, if the
+ // offer can satisfy it, the provider should not try and bring up the network.
+ // Hence tell the provider so unless it already knew.
+ if (offer.neededFor(request)) {
+ offer.onNetworkUnneeded(request);
+ }
+ }
+ }
+
private void addNetworkToLegacyTypeTracker(@NonNull final NetworkAgentInfo nai) {
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest nr = nai.requestAt(i);
@@ -8405,7 +8376,6 @@
if (VDBG || DDBG) log("updateNetworkScore for " + nai.toShortString() + " to " + score);
nai.setScore(score);
rematchAllNetworksAndRequests();
- sendUpdatedScoreToFactories(nai);
}
// Notify only this one new request of the current state. Transfer all the