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.annotation.Nullable;
21 import android.content.Context;
22 import android.net.NetworkProvider.NetworkOfferCallback;
23 import android.os.Looper;
24 import android.os.Message;
25 
26 import java.io.FileDescriptor;
27 import java.io.PrintWriter;
28 import java.util.LinkedHashMap;
29 import java.util.Map;
30 import java.util.concurrent.Executor;
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 NetworkFactoryLegacyImpl
48 class NetworkFactoryImpl extends NetworkFactoryLegacyImpl {
49     private static final boolean DBG = NetworkFactory.DBG;
50     private static final boolean VDBG = NetworkFactory.VDBG;
51 
52     // A score that will win against everything, so that score filtering will let all requests
53     // through
54     // TODO : remove this and replace with an API to listen to all requests.
55     @NonNull
56     private static final NetworkScore INVINCIBLE_SCORE =
57             new NetworkScore.Builder().setLegacyInt(1000).build();
58 
59     // TODO(b/187082970): Replace CMD_* with Handler.post(() -> { ... }) since all the CMDs do is to
60     //  run the tasks asynchronously on the Handler thread.
61 
62     /**
63      * Pass a network request to the bearer.  If the bearer believes it can
64      * satisfy the request it should connect to the network and create a
65      * NetworkAgent.  Once the NetworkAgent is fully functional it will
66      * register itself with ConnectivityService using registerNetworkAgent.
67      * If the bearer cannot immediately satisfy the request (no network,
68      * user disabled the radio, lower-scored network) it should remember
69      * any NetworkRequests it may be able to satisfy in the future.  It may
70      * disregard any that it will never be able to service, for example
71      * those requiring a different bearer.
72      * msg.obj = NetworkRequest
73      */
74     // TODO : this and CANCEL_REQUEST are only used by telephony tests. Replace it in the tests
75     // and remove them and the associated code.
76     private static final int CMD_REQUEST_NETWORK = NetworkFactory.CMD_REQUEST_NETWORK;
77 
78     /**
79      * Cancel a network request
80      * msg.obj = NetworkRequest
81      */
82     private static final int CMD_CANCEL_REQUEST = NetworkFactory.CMD_CANCEL_REQUEST;
83 
84     /**
85      * Internally used to set our best-guess score.
86      * msg.obj = new score
87      */
88     private static final int CMD_SET_SCORE = 3;
89 
90     /**
91      * Internally used to set our current filter for coarse bandwidth changes with
92      * technology changes.
93      * msg.obj = new filter
94      */
95     private static final int CMD_SET_FILTER = 4;
96 
97     /**
98      * Internally used to send the network offer associated with this factory.
99      * No arguments, will read from members
100      */
101     private static final int CMD_OFFER_NETWORK = 5;
102 
103     /**
104      * Internally used to send the request to listen to all requests.
105      * No arguments, will read from members
106      */
107     private static final int CMD_LISTEN_TO_ALL_REQUESTS = 6;
108 
109     private final Map<NetworkRequest, NetworkRequestInfo> mNetworkRequests =
110             new LinkedHashMap<>();
111 
112     @NonNull private NetworkScore mScore = new NetworkScore.Builder().setLegacyInt(0).build();
113 
114     private final NetworkOfferCallback mRequestCallback = new NetworkOfferCallback() {
115         @Override
116         public void onNetworkNeeded(@NonNull final NetworkRequest request) {
117             handleAddRequest(request);
118         }
119 
120         @Override
121         public void onNetworkUnneeded(@NonNull final NetworkRequest request) {
122             handleRemoveRequest(request);
123         }
124     };
125     @NonNull private final Executor mExecutor = command -> post(command);
126 
127 
128     // Ideally the filter argument would be non-null, but null has historically meant to see
129     // no requests and telephony passes null.
NetworkFactoryImpl(NetworkFactory parent, Looper looper, Context context, @Nullable final NetworkCapabilities filter)130     NetworkFactoryImpl(NetworkFactory parent, Looper looper, Context context,
131             @Nullable final NetworkCapabilities filter) {
132         super(parent, looper, context,
133                 null != filter ? filter :
134                         NetworkCapabilities.Builder.withoutDefaultCapabilities().build());
135     }
136 
137     /* Registers this NetworkFactory with the system. May only be called once per factory. */
register(final String logTag)138     @Override public void register(final String logTag) {
139         register(logTag, false);
140     }
141 
142     /**
143      * Registers this NetworkFactory with the system ignoring the score filter. This will let
144      * the factory always see all network requests matching its capabilities filter.
145      * May only be called once per factory.
146      */
registerIgnoringScore(final String logTag)147     @Override public void registerIgnoringScore(final String logTag) {
148         register(logTag, true);
149     }
150 
register(final String logTag, final boolean listenToAllRequests)151     private void register(final String logTag, final boolean listenToAllRequests) {
152         if (mProvider != null) {
153             throw new IllegalStateException("A NetworkFactory must only be registered once");
154         }
155         if (DBG) mParent.log("Registering NetworkFactory");
156 
157         mProvider = new NetworkProvider(mContext, NetworkFactoryImpl.this.getLooper(), logTag) {
158             @Override
159             public void onNetworkRequested(@NonNull NetworkRequest request, int score,
160                     int servingProviderId) {
161                 handleAddRequest(request);
162             }
163 
164             @Override
165             public void onNetworkRequestWithdrawn(@NonNull NetworkRequest request) {
166                 handleRemoveRequest(request);
167             }
168         };
169 
170         ((ConnectivityManager) mContext.getSystemService(
171                 Context.CONNECTIVITY_SERVICE)).registerNetworkProvider(mProvider);
172 
173         // The mScore and mCapabilityFilter members can only be accessed on the handler thread.
174         // TODO : offer a separate API to listen to all requests instead
175         if (listenToAllRequests) {
176             sendMessage(obtainMessage(CMD_LISTEN_TO_ALL_REQUESTS));
177         } else {
178             sendMessage(obtainMessage(CMD_OFFER_NETWORK));
179         }
180     }
181 
handleOfferNetwork(@onNull final NetworkScore score)182     private void handleOfferNetwork(@NonNull final NetworkScore score) {
183         mProvider.registerNetworkOffer(score, mCapabilityFilter, mExecutor, mRequestCallback);
184     }
185 
186     @Override
handleMessage(Message msg)187     public void handleMessage(Message msg) {
188         switch (msg.what) {
189             case CMD_REQUEST_NETWORK: {
190                 handleAddRequest((NetworkRequest) msg.obj);
191                 break;
192             }
193             case CMD_CANCEL_REQUEST: {
194                 handleRemoveRequest((NetworkRequest) msg.obj);
195                 break;
196             }
197             case CMD_SET_SCORE: {
198                 handleSetScore((NetworkScore) msg.obj);
199                 break;
200             }
201             case CMD_SET_FILTER: {
202                 handleSetFilter((NetworkCapabilities) msg.obj);
203                 break;
204             }
205             case CMD_OFFER_NETWORK: {
206                 handleOfferNetwork(mScore);
207                 break;
208             }
209             case CMD_LISTEN_TO_ALL_REQUESTS: {
210                 handleOfferNetwork(INVINCIBLE_SCORE);
211                 break;
212             }
213         }
214     }
215 
216     private static class NetworkRequestInfo {
217         @NonNull public final NetworkRequest request;
218         public boolean requested; // do we have a request outstanding, limited by score
219 
NetworkRequestInfo(@onNull final NetworkRequest request)220         NetworkRequestInfo(@NonNull final NetworkRequest request) {
221             this.request = request;
222             this.requested = false;
223         }
224 
225         @Override
toString()226         public String toString() {
227             return "{" + request + ", requested=" + requested + "}";
228         }
229     }
230 
231     /**
232      * Add a NetworkRequest that the bearer may want to attempt to satisfy.
233      * @see #CMD_REQUEST_NETWORK
234      *
235      * @param request the request to handle.
236      */
handleAddRequest(@onNull final NetworkRequest request)237     private void handleAddRequest(@NonNull final NetworkRequest request) {
238         NetworkRequestInfo n = mNetworkRequests.get(request);
239         if (n == null) {
240             if (DBG) mParent.log("got request " + request);
241             n = new NetworkRequestInfo(request);
242             mNetworkRequests.put(n.request, n);
243         } else {
244             if (VDBG) mParent.log("handle existing request " + request);
245         }
246         if (VDBG) mParent.log("  my score=" + mScore + ", my filter=" + mCapabilityFilter);
247 
248         if (mParent.acceptRequest(request)) {
249             n.requested = true;
250             mParent.needNetworkFor(request);
251         }
252     }
253 
handleRemoveRequest(NetworkRequest request)254     private void handleRemoveRequest(NetworkRequest request) {
255         NetworkRequestInfo n = mNetworkRequests.get(request);
256         if (n != null) {
257             mNetworkRequests.remove(request);
258             if (n.requested) mParent.releaseNetworkFor(n.request);
259         }
260     }
261 
handleSetScore(@onNull final NetworkScore score)262     private void handleSetScore(@NonNull final NetworkScore score) {
263         if (mScore.equals(score)) return;
264         mScore = score;
265         mParent.reevaluateAllRequests();
266     }
267 
handleSetFilter(@onNull final NetworkCapabilities netCap)268     private void handleSetFilter(@NonNull final NetworkCapabilities netCap) {
269         if (netCap.equals(mCapabilityFilter)) return;
270         mCapabilityFilter = netCap;
271         mParent.reevaluateAllRequests();
272     }
273 
reevaluateAllRequests()274     @Override public final void reevaluateAllRequests() {
275         if (mProvider == null) return;
276         mProvider.registerNetworkOffer(mScore, mCapabilityFilter, mExecutor, mRequestCallback);
277     }
278 
279     /**
280      * @deprecated this method was never part of the API (system or public) and is only added
281      *   for migration of existing clients.
282      */
283     @Deprecated
setScoreFilter(final int score)284     public void setScoreFilter(final int score) {
285         setScoreFilter(new NetworkScore.Builder().setLegacyInt(score).build());
286     }
287 
288     /**
289      * Set a score filter for this factory.
290      *
291      * This should include the transports the factory knows its networks will have, and
292      * an optimistic view of the attributes it may have. This does not commit the factory
293      * to being able to bring up such a network ; it only lets it avoid hearing about
294      * requests that it has no chance of fulfilling.
295      *
296      * @param score the filter
297      */
setScoreFilter(@onNull final NetworkScore score)298     @Override public void setScoreFilter(@NonNull final NetworkScore score) {
299         sendMessage(obtainMessage(CMD_SET_SCORE, score));
300     }
301 
setCapabilityFilter(NetworkCapabilities netCap)302     @Override public void setCapabilityFilter(NetworkCapabilities netCap) {
303         sendMessage(obtainMessage(CMD_SET_FILTER, new NetworkCapabilities(netCap)));
304     }
305 
getRequestCount()306     @Override public int getRequestCount() {
307         return mNetworkRequests.size();
308     }
309 
dump(FileDescriptor fd, PrintWriter writer, String[] args)310     @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
311         writer.println(toString());
312         for (NetworkRequestInfo n : mNetworkRequests.values()) {
313             writer.println("  " + n);
314         }
315     }
316 
toString()317     @Override public String toString() {
318         return "providerId="
319                 + mProvider.getProviderId() + ", ScoreFilter="
320                 + mScore + ", Filter=" + mCapabilityFilter + ", requests="
321                 + mNetworkRequests.size();
322     }
323 }
324