1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net;
18 
19 import android.annotation.NonNull;
20 import android.content.Context;
21 import android.os.Handler;
22 import android.os.Looper;
23 import android.os.Message;
24 import android.util.Log;
25 
26 import com.android.internal.annotations.VisibleForTesting;
27 
28 import java.io.FileDescriptor;
29 import java.io.PrintWriter;
30 import java.util.LinkedHashMap;
31 import java.util.Map;
32 
33 /**
34  * A NetworkFactory is an entity that creates NetworkAgent objects.
35  * The bearers register with ConnectivityService using {@link #register} and
36  * their factory will start receiving scored NetworkRequests.  NetworkRequests
37  * can be filtered 3 ways: by NetworkCapabilities, by score and more complexly by
38  * overridden function.  All of these can be dynamic - changing NetworkCapabilities
39  * or score forces re-evaluation of all current requests.
40  *
41  * If any requests pass the filter some overrideable functions will be called.
42  * If the bearer only cares about very simple start/stopNetwork callbacks, those
43  * functions can be overridden.  If the bearer needs more interaction, it can
44  * override addNetworkRequest and removeNetworkRequest which will give it each
45  * request that passes their current filters.
46  * @hide
47  **/
48 public class NetworkFactory extends Handler {
49     private static final boolean DBG = true;
50     private static final boolean VDBG = false;
51     /**
52      * Pass a network request to the bearer.  If the bearer believes it can
53      * satisfy the request it should connect to the network and create a
54      * NetworkAgent.  Once the NetworkAgent is fully functional it will
55      * register itself with ConnectivityService using registerNetworkAgent.
56      * If the bearer cannot immediately satisfy the request (no network,
57      * user disabled the radio, lower-scored network) it should remember
58      * any NetworkRequests it may be able to satisfy in the future.  It may
59      * disregard any that it will never be able to service, for example
60      * those requiring a different bearer.
61      * msg.obj = NetworkRequest
62      * msg.arg1 = score - the score of the network currently satisfying this
63      *            request.  If this bearer knows in advance it cannot
64      *            exceed this score it should not try to connect, holding the request
65      *            for the future.
66      *            Note that subsequent events may give a different (lower
67      *            or higher) score for this request, transmitted to each
68      *            NetworkFactory through additional CMD_REQUEST_NETWORK msgs
69      *            with the same NetworkRequest but an updated score.
70      *            Also, network conditions may change for this bearer
71      *            allowing for a better score in the future.
72      * msg.arg2 = the ID of the NetworkProvider currently responsible for the
73      *            NetworkAgent handling this request, or NetworkProvider.ID_NONE if none.
74      */
75     public static final int CMD_REQUEST_NETWORK = 1;
76 
77     /**
78      * Cancel a network request
79      * msg.obj = NetworkRequest
80      */
81     public static final int CMD_CANCEL_REQUEST = 2;
82 
83     /**
84      * Internally used to set our best-guess score.
85      * msg.arg1 = new score
86      */
87     private static final int CMD_SET_SCORE = 3;
88 
89     /**
90      * Internally used to set our current filter for coarse bandwidth changes with
91      * technology changes.
92      * msg.obj = new filter
93      */
94     private static final int CMD_SET_FILTER = 4;
95 
96     private final Context mContext;
97     private final String LOG_TAG;
98 
99     private final Map<NetworkRequest, NetworkRequestInfo> mNetworkRequests =
100             new LinkedHashMap<>();
101 
102     private int mScore;
103     private NetworkCapabilities mCapabilityFilter;
104 
105     private int mRefCount = 0;
106     private NetworkProvider mProvider = null;
107 
NetworkFactory(Looper looper, Context context, String logTag, NetworkCapabilities filter)108     public NetworkFactory(Looper looper, Context context, String logTag,
109             NetworkCapabilities filter) {
110         super(looper);
111         LOG_TAG = logTag;
112         mContext = context;
113         mCapabilityFilter = filter;
114     }
115 
116     /* Registers this NetworkFactory with the system. May only be called once per factory. */
register()117     public void register() {
118         if (mProvider != null) {
119             throw new IllegalStateException("A NetworkFactory must only be registered once");
120         }
121         if (DBG) log("Registering NetworkFactory");
122 
123         mProvider = new NetworkProvider(mContext, NetworkFactory.this.getLooper(), LOG_TAG) {
124             @Override
125             public void onNetworkRequested(@NonNull NetworkRequest request, int score,
126                     int servingProviderId) {
127                 handleAddRequest(request, score, servingProviderId);
128             }
129 
130             @Override
131             public void onNetworkRequestWithdrawn(@NonNull NetworkRequest request) {
132                 handleRemoveRequest(request);
133             }
134         };
135 
136         ((ConnectivityManager) mContext.getSystemService(
137             Context.CONNECTIVITY_SERVICE)).registerNetworkProvider(mProvider);
138     }
139 
140     /** Unregisters this NetworkFactory. After this call, the object can no longer be used. */
terminate()141     public void terminate() {
142         if (mProvider == null) {
143             throw new IllegalStateException("This NetworkFactory was never registered");
144         }
145         if (DBG) log("Unregistering NetworkFactory");
146 
147         ((ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
148             .unregisterNetworkProvider(mProvider);
149 
150         // Remove all pending messages, since this object cannot be reused. Any message currently
151         // being processed will continue to run.
152         removeCallbacksAndMessages(null);
153     }
154 
155     @Override
handleMessage(Message msg)156     public void handleMessage(Message msg) {
157         switch (msg.what) {
158             case CMD_REQUEST_NETWORK: {
159                 handleAddRequest((NetworkRequest) msg.obj, msg.arg1, msg.arg2);
160                 break;
161             }
162             case CMD_CANCEL_REQUEST: {
163                 handleRemoveRequest((NetworkRequest) msg.obj);
164                 break;
165             }
166             case CMD_SET_SCORE: {
167                 handleSetScore(msg.arg1);
168                 break;
169             }
170             case CMD_SET_FILTER: {
171                 handleSetFilter((NetworkCapabilities) msg.obj);
172                 break;
173             }
174         }
175     }
176 
177     private static class NetworkRequestInfo {
178         public final NetworkRequest request;
179         public int score;
180         public boolean requested; // do we have a request outstanding, limited by score
181         public int providerId;
182 
NetworkRequestInfo(NetworkRequest request, int score, int providerId)183         NetworkRequestInfo(NetworkRequest request, int score, int providerId) {
184             this.request = request;
185             this.score = score;
186             this.requested = false;
187             this.providerId = providerId;
188         }
189 
190         @Override
toString()191         public String toString() {
192             return "{" + request + ", score=" + score + ", requested=" + requested + "}";
193         }
194     }
195 
196     /**
197      * Add a NetworkRequest that the bearer may want to attempt to satisfy.
198      * @see #CMD_REQUEST_NETWORK
199      *
200      * @param request the request to handle.
201      * @param score the score of the NetworkAgent currently satisfying this request.
202      * @param servingProviderId the ID of the NetworkProvider that created the NetworkAgent
203      *        currently satisfying this request.
204      */
205     @VisibleForTesting
handleAddRequest(NetworkRequest request, int score, int servingProviderId)206     protected void handleAddRequest(NetworkRequest request, int score, int servingProviderId) {
207         NetworkRequestInfo n = mNetworkRequests.get(request);
208         if (n == null) {
209             if (DBG) {
210                 log("got request " + request + " with score " + score
211                         + " and providerId " + servingProviderId);
212             }
213             n = new NetworkRequestInfo(request, score, servingProviderId);
214             mNetworkRequests.put(n.request, n);
215         } else {
216             if (VDBG) {
217                 log("new score " + score + " for existing request " + request
218                         + " and providerId " + servingProviderId);
219             }
220             n.score = score;
221             n.providerId = servingProviderId;
222         }
223         if (VDBG) log("  my score=" + mScore + ", my filter=" + mCapabilityFilter);
224 
225         evalRequest(n);
226     }
227 
228     @VisibleForTesting
handleRemoveRequest(NetworkRequest request)229     protected void handleRemoveRequest(NetworkRequest request) {
230         NetworkRequestInfo n = mNetworkRequests.get(request);
231         if (n != null) {
232             mNetworkRequests.remove(request);
233             if (n.requested) releaseNetworkFor(n.request);
234         }
235     }
236 
handleSetScore(int score)237     private void handleSetScore(int score) {
238         mScore = score;
239         evalRequests();
240     }
241 
handleSetFilter(NetworkCapabilities netCap)242     private void handleSetFilter(NetworkCapabilities netCap) {
243         mCapabilityFilter = netCap;
244         evalRequests();
245     }
246 
247     /**
248      * Overridable function to provide complex filtering.
249      * Called for every request every time a new NetworkRequest is seen
250      * and whenever the filterScore or filterNetworkCapabilities change.
251      *
252      * acceptRequest can be overridden to provide complex filter behavior
253      * for the incoming requests
254      *
255      * For output, this class will call {@link #needNetworkFor} and
256      * {@link #releaseNetworkFor} for every request that passes the filters.
257      * If you don't need to see every request, you can leave the base
258      * implementations of those two functions and instead override
259      * {@link #startNetwork} and {@link #stopNetwork}.
260      *
261      * If you want to see every score fluctuation on every request, set
262      * your score filter to a very high number and watch {@link #needNetworkFor}.
263      *
264      * @return {@code true} to accept the request.
265      */
acceptRequest(NetworkRequest request, int score)266     public boolean acceptRequest(NetworkRequest request, int score) {
267         return true;
268     }
269 
evalRequest(NetworkRequestInfo n)270     private void evalRequest(NetworkRequestInfo n) {
271         if (VDBG) {
272             log("evalRequest");
273             log(" n.requests = " + n.requested);
274             log(" n.score = " + n.score);
275             log(" mScore = " + mScore);
276             log(" request.providerId = " + n.providerId);
277             log(" mProvider.id = " + mProvider.getProviderId());
278         }
279         if (shouldNeedNetworkFor(n)) {
280             if (VDBG) log("  needNetworkFor");
281             needNetworkFor(n.request, n.score);
282             n.requested = true;
283         } else if (shouldReleaseNetworkFor(n)) {
284             if (VDBG) log("  releaseNetworkFor");
285             releaseNetworkFor(n.request);
286             n.requested = false;
287         } else {
288             if (VDBG) log("  done");
289         }
290     }
291 
shouldNeedNetworkFor(NetworkRequestInfo n)292     private boolean shouldNeedNetworkFor(NetworkRequestInfo n) {
293         // If this request is already tracked, it doesn't qualify for need
294         return !n.requested
295             // If the score of this request is higher or equal to that of this factory and some
296             // other factory is responsible for it, then this factory should not track the request
297             // because it has no hope of satisfying it.
298             && (n.score < mScore || n.providerId == mProvider.getProviderId())
299             // If this factory can't satisfy the capability needs of this request, then it
300             // should not be tracked.
301             && n.request.canBeSatisfiedBy(mCapabilityFilter)
302             // Finally if the concrete implementation of the factory rejects the request, then
303             // don't track it.
304             && acceptRequest(n.request, n.score);
305     }
306 
shouldReleaseNetworkFor(NetworkRequestInfo n)307     private boolean shouldReleaseNetworkFor(NetworkRequestInfo n) {
308         // Don't release a request that's not tracked.
309         return n.requested
310             // The request should be released if it can't be satisfied by this factory. That
311             // means either of the following conditions are met :
312             // - Its score is too high to be satisfied by this factory and it's not already
313             //   assigned to the factory
314             // - This factory can't satisfy the capability needs of the request
315             // - The concrete implementation of the factory rejects the request
316             && ((n.score > mScore && n.providerId != mProvider.getProviderId())
317                     || !n.request.canBeSatisfiedBy(mCapabilityFilter)
318                     || !acceptRequest(n.request, n.score));
319     }
320 
evalRequests()321     private void evalRequests() {
322         for (NetworkRequestInfo n : mNetworkRequests.values()) {
323             evalRequest(n);
324         }
325     }
326 
327     /**
328      * Post a command, on this NetworkFactory Handler, to re-evaluate all
329      * outstanding requests. Can be called from a factory implementation.
330      */
reevaluateAllRequests()331     protected void reevaluateAllRequests() {
332         post(this::evalRequests);
333     }
334 
335     /**
336      * Can be called by a factory to release a request as unfulfillable: the request will be
337      * removed, and the caller will get a
338      * {@link ConnectivityManager.NetworkCallback#onUnavailable()} callback after this function
339      * returns.
340      *
341      * Note: this should only be called by factory which KNOWS that it is the ONLY factory which
342      * is able to fulfill this request!
343      */
releaseRequestAsUnfulfillableByAnyFactory(NetworkRequest r)344     protected void releaseRequestAsUnfulfillableByAnyFactory(NetworkRequest r) {
345         post(() -> {
346             if (DBG) log("releaseRequestAsUnfulfillableByAnyFactory: " + r);
347             final NetworkProvider provider = mProvider;
348             if (provider == null) {
349                 Log.e(LOG_TAG, "Ignoring attempt to release unregistered request as unfulfillable");
350                 return;
351             }
352             provider.declareNetworkRequestUnfulfillable(r);
353         });
354     }
355 
356     // override to do simple mode (request independent)
startNetwork()357     protected void startNetwork() { }
stopNetwork()358     protected void stopNetwork() { }
359 
360     // override to do fancier stuff
needNetworkFor(NetworkRequest networkRequest, int score)361     protected void needNetworkFor(NetworkRequest networkRequest, int score) {
362         if (++mRefCount == 1) startNetwork();
363     }
364 
releaseNetworkFor(NetworkRequest networkRequest)365     protected void releaseNetworkFor(NetworkRequest networkRequest) {
366         if (--mRefCount == 0) stopNetwork();
367     }
368 
setScoreFilter(int score)369     public void setScoreFilter(int score) {
370         sendMessage(obtainMessage(CMD_SET_SCORE, score, 0));
371     }
372 
setCapabilityFilter(NetworkCapabilities netCap)373     public void setCapabilityFilter(NetworkCapabilities netCap) {
374         sendMessage(obtainMessage(CMD_SET_FILTER, new NetworkCapabilities(netCap)));
375     }
376 
377     @VisibleForTesting
getRequestCount()378     protected int getRequestCount() {
379         return mNetworkRequests.size();
380     }
381 
382     /* TODO: delete when all callers have migrated to NetworkProvider IDs. */
getSerialNumber()383     public int getSerialNumber() {
384         return mProvider.getProviderId();
385     }
386 
getProvider()387     public NetworkProvider getProvider() {
388         return mProvider;
389     }
390 
log(String s)391     protected void log(String s) {
392         Log.d(LOG_TAG, s);
393     }
394 
dump(FileDescriptor fd, PrintWriter writer, String[] args)395     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
396         writer.println(toString());
397         for (NetworkRequestInfo n : mNetworkRequests.values()) {
398             writer.println("  " + n);
399         }
400     }
401 
402     @Override
toString()403     public String toString() {
404         return "{" + LOG_TAG + " - providerId="
405                 + mProvider.getProviderId() + ", ScoreFilter="
406                 + mScore + ", Filter=" + mCapabilityFilter + ", requests="
407                 + mNetworkRequests.size() + ", refCount=" + mRefCount
408                 + "}";
409     }
410 }
411