1 /*
2  * Copyright (C) 2017 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 com.android.networkstack.tethering;
18 
19 import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
20 import static android.net.ConnectivityManager.TYPE_ETHERNET;
21 import static android.net.ConnectivityManager.TYPE_MOBILE;
22 import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
23 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
24 import static android.net.ConnectivityManager.TYPE_WIFI;
25 import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
26 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
27 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
28 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
29 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
30 
31 import android.content.Context;
32 import android.net.ConnectivityManager;
33 import android.net.ConnectivityManager.NetworkCallback;
34 import android.net.IpPrefix;
35 import android.net.LinkProperties;
36 import android.net.Network;
37 import android.net.NetworkCapabilities;
38 import android.net.NetworkRequest;
39 import android.net.util.PrefixUtils;
40 import android.net.util.SharedLog;
41 import android.os.Handler;
42 import android.util.Log;
43 import android.util.SparseIntArray;
44 
45 import com.android.internal.annotations.VisibleForTesting;
46 import com.android.internal.util.StateMachine;
47 
48 import java.util.HashMap;
49 import java.util.HashSet;
50 import java.util.Set;
51 
52 
53 /**
54  * A class to centralize all the network and link properties information
55  * pertaining to the current and any potential upstream network.
56  *
57  * The owner of UNM gets it to register network callbacks by calling the
58  * following methods :
59  * Calling #startTrackDefaultNetwork() to track the system default network.
60  * Calling #startObserveAllNetworks() to observe all networks. Listening all
61  * networks is necessary while the expression of preferred upstreams remains
62  * a list of legacy connectivity types.  In future, this can be revisited.
63  * Calling #registerMobileNetworkRequest() to bring up mobile DUN/HIPRI network.
64  *
65  * The methods and data members of this class are only to be accessed and
66  * modified from the tethering main state machine thread. Any other
67  * access semantics would necessitate the addition of locking.
68  *
69  * TODO: Move upstream selection logic here.
70  *
71  * All callback methods are run on the same thread as the specified target
72  * state machine.  This class does not require locking when accessed from this
73  * thread.  Access from other threads is not advised.
74  *
75  * @hide
76  */
77 public class UpstreamNetworkMonitor {
78     private static final String TAG = UpstreamNetworkMonitor.class.getSimpleName();
79     private static final boolean DBG = false;
80     private static final boolean VDBG = false;
81 
82     public static final int EVENT_ON_CAPABILITIES   = 1;
83     public static final int EVENT_ON_LINKPROPERTIES = 2;
84     public static final int EVENT_ON_LOST           = 3;
85     public static final int NOTIFY_LOCAL_PREFIXES   = 10;
86     // This value is used by deprecated preferredUpstreamIfaceTypes selection which is default
87     // disabled.
88     @VisibleForTesting
89     public static final int TYPE_NONE = -1;
90 
91     private static final int CALLBACK_LISTEN_ALL = 1;
92     private static final int CALLBACK_DEFAULT_INTERNET = 2;
93     private static final int CALLBACK_MOBILE_REQUEST = 3;
94 
95     private static final SparseIntArray sLegacyTypeToTransport = new SparseIntArray();
96     static {
sLegacyTypeToTransport.put(TYPE_MOBILE, NetworkCapabilities.TRANSPORT_CELLULAR)97         sLegacyTypeToTransport.put(TYPE_MOBILE,       NetworkCapabilities.TRANSPORT_CELLULAR);
sLegacyTypeToTransport.put(TYPE_MOBILE_DUN, NetworkCapabilities.TRANSPORT_CELLULAR)98         sLegacyTypeToTransport.put(TYPE_MOBILE_DUN,   NetworkCapabilities.TRANSPORT_CELLULAR);
sLegacyTypeToTransport.put(TYPE_MOBILE_HIPRI, NetworkCapabilities.TRANSPORT_CELLULAR)99         sLegacyTypeToTransport.put(TYPE_MOBILE_HIPRI, NetworkCapabilities.TRANSPORT_CELLULAR);
sLegacyTypeToTransport.put(TYPE_WIFI, NetworkCapabilities.TRANSPORT_WIFI)100         sLegacyTypeToTransport.put(TYPE_WIFI,         NetworkCapabilities.TRANSPORT_WIFI);
sLegacyTypeToTransport.put(TYPE_BLUETOOTH, NetworkCapabilities.TRANSPORT_BLUETOOTH)101         sLegacyTypeToTransport.put(TYPE_BLUETOOTH,    NetworkCapabilities.TRANSPORT_BLUETOOTH);
sLegacyTypeToTransport.put(TYPE_ETHERNET, NetworkCapabilities.TRANSPORT_ETHERNET)102         sLegacyTypeToTransport.put(TYPE_ETHERNET,     NetworkCapabilities.TRANSPORT_ETHERNET);
103     }
104 
105     private final Context mContext;
106     private final SharedLog mLog;
107     private final StateMachine mTarget;
108     private final Handler mHandler;
109     private final int mWhat;
110     private final HashMap<Network, UpstreamNetworkState> mNetworkMap = new HashMap<>();
111     private HashSet<IpPrefix> mLocalPrefixes;
112     private ConnectivityManager mCM;
113     private EntitlementManager mEntitlementMgr;
114     private NetworkCallback mListenAllCallback;
115     private NetworkCallback mDefaultNetworkCallback;
116     private NetworkCallback mMobileNetworkCallback;
117     private boolean mDunRequired;
118     // Whether the current default upstream is mobile or not.
119     private boolean mIsDefaultCellularUpstream;
120     // The current system default network (not really used yet).
121     private Network mDefaultInternetNetwork;
122     // The current upstream network used for tethering.
123     private Network mTetheringUpstreamNetwork;
124 
UpstreamNetworkMonitor(Context ctx, StateMachine tgt, SharedLog log, int what)125     public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, SharedLog log, int what) {
126         mContext = ctx;
127         mTarget = tgt;
128         mHandler = mTarget.getHandler();
129         mLog = log.forSubComponent(TAG);
130         mWhat = what;
131         mLocalPrefixes = new HashSet<>();
132         mIsDefaultCellularUpstream = false;
133     }
134 
135     @VisibleForTesting
UpstreamNetworkMonitor( ConnectivityManager cm, StateMachine tgt, SharedLog log, int what)136     public UpstreamNetworkMonitor(
137             ConnectivityManager cm, StateMachine tgt, SharedLog log, int what) {
138         this((Context) null, tgt, log, what);
139         mCM = cm;
140     }
141 
142     /**
143      * Tracking the system default network. This method should be called when system is ready.
144      *
145      * @param defaultNetworkRequest should be the same as ConnectivityService default request
146      * @param entitle a EntitlementManager object to communicate between EntitlementManager and
147      * UpstreamNetworkMonitor
148      */
startTrackDefaultNetwork(NetworkRequest defaultNetworkRequest, EntitlementManager entitle)149     public void startTrackDefaultNetwork(NetworkRequest defaultNetworkRequest,
150             EntitlementManager entitle) {
151 
152         // defaultNetworkRequest is not really a "request", just a way of tracking the system
153         // default network. It's guaranteed not to actually bring up any networks because it's
154         // the should be the same request as the ConnectivityService default request, and thus
155         // shares fate with it. We can't use registerDefaultNetworkCallback because it will not
156         // track the system default network if there is a VPN that applies to our UID.
157         if (mDefaultNetworkCallback == null) {
158             mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_DEFAULT_INTERNET);
159             cm().requestNetwork(defaultNetworkRequest, mDefaultNetworkCallback, mHandler);
160         }
161         if (mEntitlementMgr == null) {
162             mEntitlementMgr = entitle;
163         }
164     }
165 
166     /** Listen all networks. */
startObserveAllNetworks()167     public void startObserveAllNetworks() {
168         stop();
169 
170         final NetworkRequest listenAllRequest = new NetworkRequest.Builder()
171                 .clearCapabilities().build();
172         mListenAllCallback = new UpstreamNetworkCallback(CALLBACK_LISTEN_ALL);
173         cm().registerNetworkCallback(listenAllRequest, mListenAllCallback, mHandler);
174     }
175 
176     /**
177      * Stop tracking candidate tethering upstreams and release mobile network request.
178      * Note: this function is used when tethering is stopped because tethering do not need to
179      * choose upstream anymore. But it would not stop default network tracking because
180      * EntitlementManager may need to know default network to decide whether to request entitlement
181      * check even tethering is not active yet.
182      */
stop()183     public void stop() {
184         releaseMobileNetworkRequest();
185 
186         releaseCallback(mListenAllCallback);
187         mListenAllCallback = null;
188 
189         mTetheringUpstreamNetwork = null;
190         mNetworkMap.clear();
191     }
192 
193     /** Setup or teardown DUN connection according to |dunRequired|. */
updateMobileRequiresDun(boolean dunRequired)194     public void updateMobileRequiresDun(boolean dunRequired) {
195         final boolean valueChanged = (mDunRequired != dunRequired);
196         mDunRequired = dunRequired;
197         if (valueChanged && mobileNetworkRequested()) {
198             releaseMobileNetworkRequest();
199             registerMobileNetworkRequest();
200         }
201     }
202 
203     /** Whether mobile network is requested. */
mobileNetworkRequested()204     public boolean mobileNetworkRequested() {
205         return (mMobileNetworkCallback != null);
206     }
207 
208     /** Request mobile network if mobile upstream is permitted. */
registerMobileNetworkRequest()209     public void registerMobileNetworkRequest() {
210         if (!isCellularUpstreamPermitted()) {
211             mLog.i("registerMobileNetworkRequest() is not permitted");
212             releaseMobileNetworkRequest();
213             return;
214         }
215         if (mMobileNetworkCallback != null) {
216             mLog.e("registerMobileNetworkRequest() already registered");
217             return;
218         }
219 
220         final NetworkRequest mobileUpstreamRequest;
221         if (mDunRequired) {
222             mobileUpstreamRequest = new NetworkRequest.Builder()
223                     .addCapability(NET_CAPABILITY_DUN)
224                     .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
225                     .addTransportType(TRANSPORT_CELLULAR).build();
226         } else {
227             mobileUpstreamRequest = new NetworkRequest.Builder()
228                     .addCapability(NET_CAPABILITY_INTERNET)
229                     .addTransportType(TRANSPORT_CELLULAR).build();
230         }
231 
232         // The existing default network and DUN callbacks will be notified.
233         // Therefore, to avoid duplicate notifications, we only register a no-op.
234         mMobileNetworkCallback = new UpstreamNetworkCallback(CALLBACK_MOBILE_REQUEST);
235 
236         // The following use of the legacy type system cannot be removed until
237         // upstream selection no longer finds networks by legacy type.
238         // See also http://b/34364553 .
239         final int legacyType = mDunRequired ? TYPE_MOBILE_DUN : TYPE_MOBILE_HIPRI;
240 
241         // TODO: Change the timeout from 0 (no onUnavailable callback) to some
242         // moderate callback timeout. This might be useful for updating some UI.
243         // Additionally, we log a message to aid in any subsequent debugging.
244         mLog.i("requesting mobile upstream network: " + mobileUpstreamRequest);
245 
246         cm().requestNetwork(mobileUpstreamRequest, 0, legacyType, mHandler,
247                 mMobileNetworkCallback);
248     }
249 
250     /** Release mobile network request. */
releaseMobileNetworkRequest()251     public void releaseMobileNetworkRequest() {
252         if (mMobileNetworkCallback == null) return;
253 
254         cm().unregisterNetworkCallback(mMobileNetworkCallback);
255         mMobileNetworkCallback = null;
256     }
257 
258     // So many TODOs here, but chief among them is: make this functionality an
259     // integral part of this class such that whenever a higher priority network
260     // becomes available and useful we (a) file a request to keep it up as
261     // necessary and (b) change all upstream tracking state accordingly (by
262     // passing LinkProperties up to Tethering).
263     /**
264      * Select the first available network from |perferredTypes|.
265      */
selectPreferredUpstreamType(Iterable<Integer> preferredTypes)266     public UpstreamNetworkState selectPreferredUpstreamType(Iterable<Integer> preferredTypes) {
267         final TypeStatePair typeStatePair = findFirstAvailableUpstreamByType(
268                 mNetworkMap.values(), preferredTypes, isCellularUpstreamPermitted());
269 
270         mLog.log("preferred upstream type: " + typeStatePair.type);
271 
272         switch (typeStatePair.type) {
273             case TYPE_MOBILE_DUN:
274             case TYPE_MOBILE_HIPRI:
275                 // Tethering just selected mobile upstream in spite of the default network being
276                 // not mobile. This can happen because of the priority list.
277                 // Notify EntitlementManager to check permission for using mobile upstream.
278                 if (!mIsDefaultCellularUpstream) {
279                     mEntitlementMgr.maybeRunProvisioning();
280                 }
281                 // If we're on DUN, put our own grab on it.
282                 registerMobileNetworkRequest();
283                 break;
284             case TYPE_NONE:
285                 // If we found NONE and mobile upstream is permitted we don't want to do this
286                 // as we want any previous requests to keep trying to bring up something we can use.
287                 if (!isCellularUpstreamPermitted()) releaseMobileNetworkRequest();
288                 break;
289             default:
290                 // If we've found an active upstream connection that's not DUN/HIPRI
291                 // we should stop any outstanding DUN/HIPRI requests.
292                 releaseMobileNetworkRequest();
293                 break;
294         }
295 
296         return typeStatePair.ns;
297     }
298 
299     /**
300      * Get current preferred upstream network. If default network is cellular and DUN is required,
301      * preferred upstream would be DUN otherwise preferred upstream is the same as default network.
302      * Returns null if no current upstream is available.
303      */
getCurrentPreferredUpstream()304     public UpstreamNetworkState getCurrentPreferredUpstream() {
305         final UpstreamNetworkState dfltState = (mDefaultInternetNetwork != null)
306                 ? mNetworkMap.get(mDefaultInternetNetwork)
307                 : null;
308         if (isNetworkUsableAndNotCellular(dfltState)) return dfltState;
309 
310         if (!isCellularUpstreamPermitted()) return null;
311 
312         if (!mDunRequired) return dfltState;
313 
314         // Find a DUN network. Note that code in Tethering causes a DUN request
315         // to be filed, but this might be moved into this class in future.
316         return findFirstDunNetwork(mNetworkMap.values());
317     }
318 
319     /** Tell UpstreamNetworkMonitor which network is the current upstream of tethering. */
setCurrentUpstream(Network upstream)320     public void setCurrentUpstream(Network upstream) {
321         mTetheringUpstreamNetwork = upstream;
322     }
323 
324     /** Return local prefixes. */
getLocalPrefixes()325     public Set<IpPrefix> getLocalPrefixes() {
326         return (Set<IpPrefix>) mLocalPrefixes.clone();
327     }
328 
isCellularUpstreamPermitted()329     private boolean isCellularUpstreamPermitted() {
330         if (mEntitlementMgr != null) {
331             return mEntitlementMgr.isCellularUpstreamPermitted();
332         } else {
333             // This flow should only happens in testing.
334             return true;
335         }
336     }
337 
handleAvailable(Network network)338     private void handleAvailable(Network network) {
339         if (mNetworkMap.containsKey(network)) return;
340 
341         if (VDBG) Log.d(TAG, "onAvailable for " + network);
342         mNetworkMap.put(network, new UpstreamNetworkState(null, null, network));
343     }
344 
handleNetCap(Network network, NetworkCapabilities newNc)345     private void handleNetCap(Network network, NetworkCapabilities newNc) {
346         final UpstreamNetworkState prev = mNetworkMap.get(network);
347         if (prev == null || newNc.equals(prev.networkCapabilities)) {
348             // Ignore notifications about networks for which we have not yet
349             // received onAvailable() (should never happen) and any duplicate
350             // notifications (e.g. matching more than one of our callbacks).
351             return;
352         }
353 
354         if (VDBG) {
355             Log.d(TAG, String.format("EVENT_ON_CAPABILITIES for %s: %s",
356                     network, newNc));
357         }
358 
359         mNetworkMap.put(network, new UpstreamNetworkState(
360                 prev.linkProperties, newNc, network));
361         // TODO: If sufficient information is available to select a more
362         // preferable upstream, do so now and notify the target.
363         notifyTarget(EVENT_ON_CAPABILITIES, network);
364     }
365 
handleLinkProp(Network network, LinkProperties newLp)366     private void handleLinkProp(Network network, LinkProperties newLp) {
367         final UpstreamNetworkState prev = mNetworkMap.get(network);
368         if (prev == null || newLp.equals(prev.linkProperties)) {
369             // Ignore notifications about networks for which we have not yet
370             // received onAvailable() (should never happen) and any duplicate
371             // notifications (e.g. matching more than one of our callbacks).
372             return;
373         }
374 
375         if (VDBG) {
376             Log.d(TAG, String.format("EVENT_ON_LINKPROPERTIES for %s: %s",
377                     network, newLp));
378         }
379 
380         mNetworkMap.put(network, new UpstreamNetworkState(
381                 newLp, prev.networkCapabilities, network));
382         // TODO: If sufficient information is available to select a more
383         // preferable upstream, do so now and notify the target.
384         notifyTarget(EVENT_ON_LINKPROPERTIES, network);
385     }
386 
handleLost(Network network)387     private void handleLost(Network network) {
388         // There are few TODOs within ConnectivityService's rematching code
389         // pertaining to spurious onLost() notifications.
390         //
391         // TODO: simplify this, probably if favor of code that:
392         //     - selects a new upstream if mTetheringUpstreamNetwork has
393         //       been lost (by any callback)
394         //     - deletes the entry from the map only when the LISTEN_ALL
395         //       callback gets notified.
396 
397         if (!mNetworkMap.containsKey(network)) {
398             // Ignore loss of networks about which we had not previously
399             // learned any information or for which we have already processed
400             // an onLost() notification.
401             return;
402         }
403 
404         if (VDBG) Log.d(TAG, "EVENT_ON_LOST for " + network);
405 
406         // TODO: If sufficient information is available to select a more
407         // preferable upstream, do so now and notify the target.  Likewise,
408         // if the current upstream network is gone, notify the target of the
409         // fact that we now have no upstream at all.
410         notifyTarget(EVENT_ON_LOST, mNetworkMap.remove(network));
411     }
412 
recomputeLocalPrefixes()413     private void recomputeLocalPrefixes() {
414         final HashSet<IpPrefix> localPrefixes = allLocalPrefixes(mNetworkMap.values());
415         if (!mLocalPrefixes.equals(localPrefixes)) {
416             mLocalPrefixes = localPrefixes;
417             notifyTarget(NOTIFY_LOCAL_PREFIXES, localPrefixes.clone());
418         }
419     }
420 
421     // Fetch (and cache) a ConnectivityManager only if and when we need one.
cm()422     private ConnectivityManager cm() {
423         if (mCM == null) {
424             // MUST call the String variant to be able to write unittests.
425             mCM = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
426         }
427         return mCM;
428     }
429 
430     /**
431      * A NetworkCallback class that handles information of interest directly
432      * in the thread on which it is invoked. To avoid locking, this MUST be
433      * run on the same thread as the target state machine's handler.
434      */
435     private class UpstreamNetworkCallback extends NetworkCallback {
436         private final int mCallbackType;
437 
UpstreamNetworkCallback(int callbackType)438         UpstreamNetworkCallback(int callbackType) {
439             mCallbackType = callbackType;
440         }
441 
442         @Override
onAvailable(Network network)443         public void onAvailable(Network network) {
444             handleAvailable(network);
445         }
446 
447         @Override
onCapabilitiesChanged(Network network, NetworkCapabilities newNc)448         public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) {
449             if (mCallbackType == CALLBACK_DEFAULT_INTERNET) {
450                 mDefaultInternetNetwork = network;
451                 final boolean newIsCellular = isCellular(newNc);
452                 if (mIsDefaultCellularUpstream != newIsCellular) {
453                     mIsDefaultCellularUpstream = newIsCellular;
454                     mEntitlementMgr.notifyUpstream(newIsCellular);
455                 }
456                 return;
457             }
458 
459             handleNetCap(network, newNc);
460         }
461 
462         @Override
onLinkPropertiesChanged(Network network, LinkProperties newLp)463         public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
464             if (mCallbackType == CALLBACK_DEFAULT_INTERNET) return;
465 
466             handleLinkProp(network, newLp);
467             // Any non-LISTEN_ALL callback will necessarily concern a network that will
468             // also match the LISTEN_ALL callback by construction of the LISTEN_ALL callback.
469             // So it's not useful to do this work for non-LISTEN_ALL callbacks.
470             if (mCallbackType == CALLBACK_LISTEN_ALL) {
471                 recomputeLocalPrefixes();
472             }
473         }
474 
475         @Override
onLost(Network network)476         public void onLost(Network network) {
477             if (mCallbackType == CALLBACK_DEFAULT_INTERNET) {
478                 mDefaultInternetNetwork = null;
479                 mIsDefaultCellularUpstream = false;
480                 mEntitlementMgr.notifyUpstream(false);
481                 return;
482             }
483 
484             handleLost(network);
485             // Any non-LISTEN_ALL callback will necessarily concern a network that will
486             // also match the LISTEN_ALL callback by construction of the LISTEN_ALL callback.
487             // So it's not useful to do this work for non-LISTEN_ALL callbacks.
488             if (mCallbackType == CALLBACK_LISTEN_ALL) {
489                 recomputeLocalPrefixes();
490             }
491         }
492     }
493 
releaseCallback(NetworkCallback cb)494     private void releaseCallback(NetworkCallback cb) {
495         if (cb != null) cm().unregisterNetworkCallback(cb);
496     }
497 
notifyTarget(int which, Network network)498     private void notifyTarget(int which, Network network) {
499         notifyTarget(which, mNetworkMap.get(network));
500     }
501 
notifyTarget(int which, Object obj)502     private void notifyTarget(int which, Object obj) {
503         mTarget.sendMessage(mWhat, which, 0, obj);
504     }
505 
506     private static class TypeStatePair {
507         public int type = TYPE_NONE;
508         public UpstreamNetworkState ns = null;
509     }
510 
findFirstAvailableUpstreamByType( Iterable<UpstreamNetworkState> netStates, Iterable<Integer> preferredTypes, boolean isCellularUpstreamPermitted)511     private static TypeStatePair findFirstAvailableUpstreamByType(
512             Iterable<UpstreamNetworkState> netStates, Iterable<Integer> preferredTypes,
513             boolean isCellularUpstreamPermitted) {
514         final TypeStatePair result = new TypeStatePair();
515 
516         for (int type : preferredTypes) {
517             NetworkCapabilities nc;
518             try {
519                 nc = networkCapabilitiesForType(type);
520             } catch (IllegalArgumentException iae) {
521                 Log.e(TAG, "No NetworkCapabilities mapping for legacy type: " + type);
522                 continue;
523             }
524             if (!isCellularUpstreamPermitted && isCellular(nc)) {
525                 continue;
526             }
527 
528             for (UpstreamNetworkState value : netStates) {
529                 if (!nc.satisfiedByNetworkCapabilities(value.networkCapabilities)) {
530                     continue;
531                 }
532 
533                 result.type = type;
534                 result.ns = value;
535                 return result;
536             }
537         }
538 
539         return result;
540     }
541 
allLocalPrefixes(Iterable<UpstreamNetworkState> netStates)542     private static HashSet<IpPrefix> allLocalPrefixes(Iterable<UpstreamNetworkState> netStates) {
543         final HashSet<IpPrefix> prefixSet = new HashSet<>();
544 
545         for (UpstreamNetworkState ns : netStates) {
546             final LinkProperties lp = ns.linkProperties;
547             if (lp == null) continue;
548             prefixSet.addAll(PrefixUtils.localPrefixesFrom(lp));
549         }
550 
551         return prefixSet;
552     }
553 
isCellular(UpstreamNetworkState ns)554     private static boolean isCellular(UpstreamNetworkState ns) {
555         return (ns != null) && isCellular(ns.networkCapabilities);
556     }
557 
isCellular(NetworkCapabilities nc)558     private static boolean isCellular(NetworkCapabilities nc) {
559         return (nc != null) && nc.hasTransport(TRANSPORT_CELLULAR)
560                && nc.hasCapability(NET_CAPABILITY_NOT_VPN);
561     }
562 
hasCapability(UpstreamNetworkState ns, int netCap)563     private static boolean hasCapability(UpstreamNetworkState ns, int netCap) {
564         return (ns != null) && (ns.networkCapabilities != null)
565                && ns.networkCapabilities.hasCapability(netCap);
566     }
567 
isNetworkUsableAndNotCellular(UpstreamNetworkState ns)568     private static boolean isNetworkUsableAndNotCellular(UpstreamNetworkState ns) {
569         return (ns != null) && (ns.networkCapabilities != null) && (ns.linkProperties != null)
570                && !isCellular(ns.networkCapabilities);
571     }
572 
findFirstDunNetwork( Iterable<UpstreamNetworkState> netStates)573     private static UpstreamNetworkState findFirstDunNetwork(
574             Iterable<UpstreamNetworkState> netStates) {
575         for (UpstreamNetworkState ns : netStates) {
576             if (isCellular(ns) && hasCapability(ns, NET_CAPABILITY_DUN)) return ns;
577         }
578 
579         return null;
580     }
581 
582     /**
583      * Given a legacy type (TYPE_WIFI, ...) returns the corresponding NetworkCapabilities instance.
584      * This function is used for deprecated legacy type and be disabled by default.
585      */
586     @VisibleForTesting
networkCapabilitiesForType(int type)587     public static NetworkCapabilities networkCapabilitiesForType(int type) {
588         final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
589 
590         // Map from type to transports.
591         final int notFound = -1;
592         final int transport = sLegacyTypeToTransport.get(type, notFound);
593         if (transport == notFound) {
594             throw new IllegalArgumentException("unknown legacy type: " + type);
595         }
596         builder.addTransportType(transport);
597 
598         if (type == TYPE_MOBILE_DUN) {
599             builder.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
600             // DUN is restricted network, see NetworkCapabilities#FORCE_RESTRICTED_CAPABILITIES.
601             builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
602         } else {
603             builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
604         }
605         return builder.build();
606     }
607 }
608