Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 89bd2e2a authored by Chalard Jean's avatar Chalard Jean
Browse files

[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
Change-Id: I0fe911eef2483ecbac48c733d56283b81538690a
parent 037ab3a9
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -312,9 +312,16 @@ package android.net {
    method public int getProviderId();
    method public void onNetworkRequestWithdrawn(@NonNull android.net.NetworkRequest);
    method public void onNetworkRequested(@NonNull android.net.NetworkRequest, @IntRange(from=0, to=99) int, int);
    method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void registerNetworkOffer(@NonNull android.net.NetworkScore, @NonNull android.net.NetworkCapabilities, @NonNull java.util.concurrent.Executor, @NonNull android.net.NetworkProvider.NetworkOfferCallback);
    method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void unregisterNetworkOffer(@NonNull android.net.NetworkProvider.NetworkOfferCallback);
    field public static final int ID_NONE = -1; // 0xffffffff
  }

  public static interface NetworkProvider.NetworkOfferCallback {
    method public void onNetworkNeeded(@NonNull android.net.NetworkRequest);
    method public void onNetworkUnneeded(@NonNull android.net.NetworkRequest);
  }

  public class NetworkReleasedException extends java.lang.Exception {
  }

+2 −2
Original line number Diff line number Diff line
@@ -3345,7 +3345,7 @@ public class ConnectivityManager {
     * @param score The prospective score of the network.
     * @param caps The prospective capabilities of the network.
     * @param callback The callback to call when this offer is needed or unneeded.
     * @hide
     * @hide exposed via the NetworkProvider class.
     */
    @RequiresPermission(anyOf = {
            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
@@ -3368,7 +3368,7 @@ public class ConnectivityManager {
     *
     * @param callback The callback passed at registration time. This must be the same object
     *                 that was passed to {@link #offerNetwork}
     * @hide
     * @hide exposed via the NetworkProvider class.
     */
    public void unofferNetwork(@NonNull final INetworkOfferCallback callback) {
        try {
+1 −3
Original line number Diff line number Diff line
@@ -51,10 +51,8 @@ oneway interface INetworkOfferCallback {
    /**
     * Called when a network for this offer is needed to fulfill this request.
     * @param networkRequest the request to satisfy
     * @param providerId the ID of the provider currently satisfying
     *          this request, or NetworkProvider.ID_NONE if none.
     */
    void onNetworkNeeded(in NetworkRequest networkRequest, int providerId);
    void onNetworkNeeded(in NetworkRequest networkRequest);

    /**
     * Informs the registrant that the offer is no longer valuable to fulfill this request.
+20 −14
Original line number Diff line number Diff line
@@ -168,12 +168,16 @@ public class NetworkProvider {
    }

    /** @hide */
    // TODO : make @SystemApi when the impl is complete
    @SystemApi
    public interface NetworkOfferCallback {
        /** Called by the system when a network for this offer is needed to satisfy some
         *  networking request. */
        void onNetworkNeeded(@NonNull NetworkRequest request, int providerId);
        /** Called by the system when this offer is no longer valuable for this request. */
        /**
         * Called by the system when a network for this offer is needed to satisfy some
         * networking request.
         */
        void onNetworkNeeded(@NonNull NetworkRequest request);
        /**
         * Called by the system when this offer is no longer valuable for this request.
         */
        void onNetworkUnneeded(@NonNull NetworkRequest request);
    }

@@ -188,9 +192,8 @@ public class NetworkProvider {
        }

        @Override
        public void onNetworkNeeded(final @NonNull NetworkRequest request,
                final int providerId) {
            mExecutor.execute(() -> callback.onNetworkNeeded(request, providerId));
        public void onNetworkNeeded(final @NonNull NetworkRequest request) {
            mExecutor.execute(() -> callback.onNetworkNeeded(request));
        }

        @Override
@@ -254,8 +257,11 @@ public class NetworkProvider {
     *
     * The capabilities and score act as filters as to what requests the provider will see.
     * They are not promises, but for best performance, the providers should strive to put
     * as much known information as possible in the offer. For capabilities in particular, it
     * should put all NetworkAgent-managed capabilities a network may have, even if it doesn't
     * as much known information as possible in the offer. For the score, it should put as
     * strong a score as the networks will have, since this will filter what requests the
     * provider sees – it's not a promise, it only serves to avoid sending requests that
     * the provider can't ever hope to satisfy better than any current network. For capabilities,
     * it should put all NetworkAgent-managed capabilities a network may have, even if it doesn't
     * have them at first. This applies to INTERNET, for example ; if a provider thinks the
     * network it can bring up for this offer may offer Internet access it should include the
     * INTERNET bit. It's fine if the brought up network ends up not actually having INTERNET.
@@ -268,9 +274,9 @@ public class NetworkProvider {
     *
     * @hide
     */
    // TODO : make @SystemApi when the impl is complete
    @SystemApi
    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
    public void offerNetwork(@NonNull final NetworkScore score,
    public void registerNetworkOffer(@NonNull final NetworkScore score,
            @NonNull final NetworkCapabilities caps, @NonNull final Executor executor,
            @NonNull final NetworkOfferCallback callback) {
        // Can't offer a network with a provider that is not yet registered or already unregistered.
@@ -307,9 +313,9 @@ public class NetworkProvider {
     *
     * @hide
     */
    // TODO : make @SystemApi when the impl is complete
    @SystemApi
    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
    public void unofferNetwork(final @NonNull NetworkOfferCallback callback) {
    public void unregisterNetworkOffer(final @NonNull NetworkOfferCallback callback) {
        final NetworkOfferCallbackProxy proxy = findProxyForCallback(callback);
        if (null == proxy) return;
        mProxies.remove(proxy);
+128 −158
Original line number Diff line number Diff line
@@ -3259,8 +3259,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
                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
@@ -3681,9 +3679,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
            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) {
@@ -3811,16 +3812,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
        }

        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);
            }
        }
    }
@@ -4028,7 +4026,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
            }
        }

        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) {
@@ -4041,20 +4047,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
        }
    }

    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.
@@ -4175,7 +4167,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
            nai.networkAgentConfig.acceptPartialConnectivity = accept;
            nai.updateScoreForNetworkAgentConfigUpdate();
            rematchAllNetworksAndRequests();
            sendUpdatedScoreToFactories(nai);
        }

        if (always) {
@@ -4243,7 +4234,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
        if (!nai.avoidUnvalidated) {
            nai.avoidUnvalidated = true;
            rematchAllNetworksAndRequests();
            sendUpdatedScoreToFactories(nai);
        }
    }

@@ -4348,14 +4338,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
        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
@@ -5349,24 +5334,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
            }
        }

        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);
@@ -6055,7 +6022,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
        if (DBG) log("Got NetworkProvider Messenger for " + npi.name);
        mNetworkProviderInfos.put(npi.messenger, npi);
        npi.connect(mContext, mTrackerHandler);
        sendAllRequestsToProvider(npi);
    }

    @Override
@@ -6077,6 +6043,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
    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));
@@ -6101,7 +6070,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
                toRemove.add(noi);
            }
        }
        for (NetworkOfferInfo noi : toRemove) {
        for (final NetworkOfferInfo noi : toRemove) {
            handleUnregisterNetworkOffer(noi);
        }
        if (DBG) log("unregisterNetworkProvider for " + npi.name);
@@ -6502,7 +6471,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
            return;
        }
        mNetworkOffers.add(noi);
        // TODO : send requests to the provider.
        issueNetworkNeeds(noi);
    }

    private void handleUnregisterNetworkOffer(@NonNull final NetworkOfferInfo noi) {
@@ -7277,100 +7246,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
        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) {
@@ -7862,6 +7737,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
            log(changes.toString()); // Shorter form, only one line of log
        }
        applyNetworkReassignment(changes, now);
        issueNetworkNeeds();
    }

    private void applyNetworkReassignment(@NonNull final NetworkReassignment changes,
@@ -7893,12 +7769,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
        // 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 {
@@ -8035,6 +7905,107 @@ public class ConnectivityService extends IConnectivityManager.Stub
        }
    }

    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);
@@ -8200,7 +8171,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
        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
Loading