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