summaryrefslogtreecommitdiff
path: root/service/src/com/android/server/connectivity/NetworkOffer.java
blob: eea382e2f650aca8756e8f24b14c880b67fd1b2c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.connectivity;

import android.annotation.NonNull;
import android.net.INetworkOfferCallback;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.os.RemoteException;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

/**
 * Represents an offer made by a NetworkProvider to create a network if a need arises.
 *
 * This class contains the prospective score and capabilities of the network. The provider
 * is not obligated to caps able to create a network satisfying this, nor to build a network
 * with the exact score and/or capabilities passed ; after all, not all providers know in
 * advance what a network will look like after it's connected. Instead, this is meant as a
 * filter to limit requests sent to the provider by connectivity to those that this offer stands
 * a chance to fulfill.
 *
 * @see NetworkProvider#offerNetwork.
 *
 * @hide
 */
public class NetworkOffer implements NetworkRanker.Scoreable {
    @NonNull public final FullScore score;
    @NonNull public final NetworkCapabilities caps;
    @NonNull public final INetworkOfferCallback callback;
    @NonNull public final int providerId;
    // While this could, in principle, be deduced from the old values of the satisfying networks,
    // doing so would add a lot of complexity and performance penalties. For each request, the
    // ranker would have to run again to figure out if this offer used to be able to beat the
    // previous satisfier to know if there is a change in whether this offer is now needed ;
    // besides, there would be a need to handle an edge case when a new request comes online,
    // where it's not satisfied before the first rematch, where starting to satisfy a request
    // should not result in sending unneeded to this offer. This boolean, while requiring that
    // the offers are only ever manipulated on the CS thread, is by far a simpler and
    // economical solution.
    private final Set<NetworkRequest> mCurrentlyNeeded = new HashSet<>();

    public NetworkOffer(@NonNull final FullScore score,
            @NonNull final NetworkCapabilities caps,
            @NonNull final INetworkOfferCallback callback,
            @NonNull final int providerId) {
        this.score = Objects.requireNonNull(score);
        this.caps = Objects.requireNonNull(caps);
        this.callback = Objects.requireNonNull(callback);
        this.providerId = providerId;
    }

    /**
     * Get the score filter of this offer
     */
    @Override @NonNull public FullScore getScore() {
        return score;
    }

    /**
     * Get the capabilities filter of this offer
     */
    @Override @NonNull public NetworkCapabilities getCapsNoCopy() {
        return caps;
    }

    /**
     * Tell the provider for this offer that the network is needed for a request.
     * @param request the request for which the offer is needed
     */
    public void onNetworkNeeded(@NonNull final NetworkRequest request) {
        if (mCurrentlyNeeded.contains(request)) {
            throw new IllegalStateException("Network already needed");
        }
        mCurrentlyNeeded.add(request);
        try {
            callback.onNetworkNeeded(request);
        } catch (final RemoteException e) {
            // The provider is dead. It will be removed by the death recipient.
        }
    }

    /**
     * Tell the provider for this offer that the network is no longer needed for this request.
     *
     * onNetworkNeeded will have been called with the same request before.
     *
     * @param request the request
     */
    public void onNetworkUnneeded(@NonNull final NetworkRequest request) {
        if (!mCurrentlyNeeded.contains(request)) {
            throw new IllegalStateException("Network already unneeded");
        }
        mCurrentlyNeeded.remove(request);
        try {
            callback.onNetworkUnneeded(request);
        } catch (final RemoteException e) {
            // The provider is dead. It will be removed by the death recipient.
        }
    }

    /**
     * Returns whether this offer is currently needed for this request.
     * @param request the request
     * @return whether the offer is currently considered needed
     */
    public boolean neededFor(@NonNull final NetworkRequest request) {
        return mCurrentlyNeeded.contains(request);
    }

    /**
     * Migrate from, and take over, a previous offer.
     *
     * When an updated offer is sent from a provider, call this method on the new offer, passing
     * the old one, to take over the state.
     *
     * @param previousOffer the previous offer
     */
    public void migrateFrom(@NonNull final NetworkOffer previousOffer) {
        if (!callback.asBinder().equals(previousOffer.callback.asBinder())) {
            throw new IllegalArgumentException("Can only migrate from a previous version of"
                    + " the same offer");
        }
        mCurrentlyNeeded.clear();
        mCurrentlyNeeded.addAll(previousOffer.mCurrentlyNeeded);
    }

    @Override
    public String toString() {
        final ArrayList<Integer> neededRequestIds = new ArrayList<>();
        for (final NetworkRequest request : mCurrentlyNeeded) {
            neededRequestIds.add(request.requestId);
        }
        return "NetworkOffer [ Provider Id (" + providerId + ") " + score + " Caps "
                + caps + " Needed by " + neededRequestIds + "]";
    }
}