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