• 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.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 androidx.annotation.NonNull;
46 import androidx.annotation.Nullable;
47 
48 import com.android.internal.annotations.VisibleForTesting;
49 import com.android.internal.util.StateMachine;
50 import com.android.networkstack.apishim.ConnectivityManagerShimImpl;
51 import com.android.networkstack.apishim.common.ConnectivityManagerShim;
52 import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
53 
54 import java.util.HashMap;
55 import java.util.HashSet;
56 import java.util.Objects;
57 import java.util.Set;
58 
59 
60 /**
61  * A class to centralize all the network and link properties information
62  * pertaining to the current and any potential upstream network.
63  *
64  * The owner of UNM gets it to register network callbacks by calling the
65  * following methods :
66  * Calling #startTrackDefaultNetwork() to track the system default network.
67  * Calling #startObserveAllNetworks() to observe all networks. Listening all
68  * networks is necessary while the expression of preferred upstreams remains
69  * a list of legacy connectivity types.  In future, this can be revisited.
70  * Calling #setTryCell() to request bringing up mobile DUN or HIPRI.
71  *
72  * The methods and data members of this class are only to be accessed and
73  * modified from the tethering main state machine thread. Any other
74  * access semantics would necessitate the addition of locking.
75  *
76  * TODO: Move upstream selection logic here.
77  *
78  * All callback methods are run on the same thread as the specified target
79  * state machine.  This class does not require locking when accessed from this
80  * thread.  Access from other threads is not advised.
81  *
82  * @hide
83  */
84 public class UpstreamNetworkMonitor {
85     private static final String TAG = UpstreamNetworkMonitor.class.getSimpleName();
86     private static final boolean DBG = false;
87     private static final boolean VDBG = false;
88 
89     public static final int EVENT_ON_CAPABILITIES   = 1;
90     public static final int EVENT_ON_LINKPROPERTIES = 2;
91     public static final int EVENT_ON_LOST           = 3;
92     public static final int EVENT_DEFAULT_SWITCHED  = 4;
93     public static final int NOTIFY_LOCAL_PREFIXES   = 10;
94     // This value is used by deprecated preferredUpstreamIfaceTypes selection which is default
95     // disabled.
96     @VisibleForTesting
97     public static final int TYPE_NONE = -1;
98 
99     private static final int CALLBACK_LISTEN_ALL = 1;
100     private static final int CALLBACK_DEFAULT_INTERNET = 2;
101     private static final int CALLBACK_MOBILE_REQUEST = 3;
102 
103     private static final SparseIntArray sLegacyTypeToTransport = new SparseIntArray();
104     static {
sLegacyTypeToTransport.put(TYPE_MOBILE, NetworkCapabilities.TRANSPORT_CELLULAR)105         sLegacyTypeToTransport.put(TYPE_MOBILE,       NetworkCapabilities.TRANSPORT_CELLULAR);
sLegacyTypeToTransport.put(TYPE_MOBILE_DUN, NetworkCapabilities.TRANSPORT_CELLULAR)106         sLegacyTypeToTransport.put(TYPE_MOBILE_DUN,   NetworkCapabilities.TRANSPORT_CELLULAR);
sLegacyTypeToTransport.put(TYPE_MOBILE_HIPRI, NetworkCapabilities.TRANSPORT_CELLULAR)107         sLegacyTypeToTransport.put(TYPE_MOBILE_HIPRI, NetworkCapabilities.TRANSPORT_CELLULAR);
sLegacyTypeToTransport.put(TYPE_WIFI, NetworkCapabilities.TRANSPORT_WIFI)108         sLegacyTypeToTransport.put(TYPE_WIFI,         NetworkCapabilities.TRANSPORT_WIFI);
sLegacyTypeToTransport.put(TYPE_BLUETOOTH, NetworkCapabilities.TRANSPORT_BLUETOOTH)109         sLegacyTypeToTransport.put(TYPE_BLUETOOTH,    NetworkCapabilities.TRANSPORT_BLUETOOTH);
sLegacyTypeToTransport.put(TYPE_ETHERNET, NetworkCapabilities.TRANSPORT_ETHERNET)110         sLegacyTypeToTransport.put(TYPE_ETHERNET,     NetworkCapabilities.TRANSPORT_ETHERNET);
111     }
112 
113     private final Context mContext;
114     private final SharedLog mLog;
115     private final StateMachine mTarget;
116     private final Handler mHandler;
117     private final int mWhat;
118     private final HashMap<Network, UpstreamNetworkState> mNetworkMap = new HashMap<>();
119     private HashSet<IpPrefix> mLocalPrefixes;
120     private ConnectivityManager mCM;
121     private EntitlementManager mEntitlementMgr;
122     private NetworkCallback mListenAllCallback;
123     private NetworkCallback mDefaultNetworkCallback;
124     private NetworkCallback mMobileNetworkCallback;
125 
126     /** Whether Tethering has requested a cellular upstream. */
127     private boolean mTryCell;
128     /** Whether the carrier requires DUN. */
129     private boolean mDunRequired;
130     /** Whether automatic upstream selection is enabled. */
131     private boolean mAutoUpstream;
132 
133     // Whether the current default upstream is mobile or not.
134     private boolean mIsDefaultCellularUpstream;
135     // The current system default network (not really used yet).
136     private Network mDefaultInternetNetwork;
137     // The current upstream network used for tethering.
138     private Network mTetheringUpstreamNetwork;
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         try {
166             mCmShim.registerSystemDefaultNetworkCallback(mDefaultNetworkCallback, mHandler);
167         } catch (UnsupportedApiLevelException e) {
168             Log.wtf(TAG, "registerSystemDefaultNetworkCallback is not supported");
169             return;
170         }
171         if (mEntitlementMgr == null) {
172             mEntitlementMgr = entitle;
173         }
174     }
175 
176     /** Listen all networks. */
startObserveAllNetworks()177     public void startObserveAllNetworks() {
178         stop();
179 
180         final NetworkRequest listenAllRequest = new NetworkRequest.Builder()
181                 .clearCapabilities().build();
182         mListenAllCallback = new UpstreamNetworkCallback(CALLBACK_LISTEN_ALL);
183         cm().registerNetworkCallback(listenAllRequest, mListenAllCallback, mHandler);
184     }
185 
186     /**
187      * Stop tracking candidate tethering upstreams and release mobile network request.
188      * Note: this function is used when tethering is stopped because tethering do not need to
189      * choose upstream anymore. But it would not stop default network tracking because
190      * EntitlementManager may need to know default network to decide whether to request entitlement
191      * check even tethering is not active yet.
192      */
stop()193     public void stop() {
194         setTryCell(false);
195 
196         releaseCallback(mListenAllCallback);
197         mListenAllCallback = null;
198 
199         mTetheringUpstreamNetwork = null;
200         mNetworkMap.clear();
201     }
202 
reevaluateUpstreamRequirements(boolean tryCell, boolean autoUpstream, boolean dunRequired)203     private void reevaluateUpstreamRequirements(boolean tryCell, boolean autoUpstream,
204             boolean dunRequired) {
205         final boolean mobileRequestRequired = tryCell && (dunRequired || !autoUpstream);
206         final boolean dunRequiredChanged = (mDunRequired != dunRequired);
207 
208         mTryCell = tryCell;
209         mDunRequired = dunRequired;
210         mAutoUpstream = autoUpstream;
211 
212         if (mobileRequestRequired && !mobileNetworkRequested()) {
213             registerMobileNetworkRequest();
214         } else if (mobileNetworkRequested() && !mobileRequestRequired) {
215             releaseMobileNetworkRequest();
216         } else if (mobileNetworkRequested() && dunRequiredChanged) {
217             releaseMobileNetworkRequest();
218             if (mobileRequestRequired) {
219                 registerMobileNetworkRequest();
220             }
221         }
222     }
223 
224     /**
225      * Informs UpstreamNetworkMonitor that a cellular upstream is desired.
226      *
227      * This may result in filing a NetworkRequest for DUN if it is required, or for MOBILE_HIPRI if
228      * automatic upstream selection is disabled and MOBILE_HIPRI is the preferred upstream.
229      */
setTryCell(boolean tryCell)230     public void setTryCell(boolean tryCell) {
231         reevaluateUpstreamRequirements(tryCell, mAutoUpstream, mDunRequired);
232     }
233 
234     /** Informs UpstreamNetworkMonitor of upstream configuration parameters. */
setUpstreamConfig(boolean autoUpstream, boolean dunRequired)235     public void setUpstreamConfig(boolean autoUpstream, boolean dunRequired) {
236         reevaluateUpstreamRequirements(mTryCell, autoUpstream, dunRequired);
237     }
238 
239     /** Whether mobile network is requested. */
mobileNetworkRequested()240     public boolean mobileNetworkRequested() {
241         return (mMobileNetworkCallback != null);
242     }
243 
244     /** Request mobile network if mobile upstream is permitted. */
registerMobileNetworkRequest()245     private void registerMobileNetworkRequest() {
246         if (!isCellularUpstreamPermitted()) {
247             mLog.i("registerMobileNetworkRequest() is not permitted");
248             releaseMobileNetworkRequest();
249             return;
250         }
251         if (mMobileNetworkCallback != null) {
252             mLog.e("registerMobileNetworkRequest() already registered");
253             return;
254         }
255 
256         final NetworkRequest mobileUpstreamRequest;
257         if (mDunRequired) {
258             mobileUpstreamRequest = new NetworkRequest.Builder()
259                     .addCapability(NET_CAPABILITY_DUN)
260                     .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
261                     .addTransportType(TRANSPORT_CELLULAR).build();
262         } else {
263             mobileUpstreamRequest = new NetworkRequest.Builder()
264                     .addCapability(NET_CAPABILITY_INTERNET)
265                     .addTransportType(TRANSPORT_CELLULAR).build();
266         }
267 
268         // The existing default network and DUN callbacks will be notified.
269         // Therefore, to avoid duplicate notifications, we only register a no-op.
270         mMobileNetworkCallback = new UpstreamNetworkCallback(CALLBACK_MOBILE_REQUEST);
271 
272         // The following use of the legacy type system cannot be removed until
273         // upstream selection no longer finds networks by legacy type.
274         // See also http://b/34364553 .
275         final int legacyType = mDunRequired ? TYPE_MOBILE_DUN : TYPE_MOBILE_HIPRI;
276 
277         // TODO: Change the timeout from 0 (no onUnavailable callback) to some
278         // moderate callback timeout. This might be useful for updating some UI.
279         // Additionally, we log a message to aid in any subsequent debugging.
280         mLog.i("requesting mobile upstream network: " + mobileUpstreamRequest
281                 + " mTryCell=" + mTryCell + " mAutoUpstream=" + mAutoUpstream
282                 + " mDunRequired=" + mDunRequired);
283 
284         cm().requestNetwork(mobileUpstreamRequest, 0, legacyType, mHandler,
285                 mMobileNetworkCallback);
286     }
287 
288     /** Release mobile network request. */
releaseMobileNetworkRequest()289     private void releaseMobileNetworkRequest() {
290         if (mMobileNetworkCallback == null) return;
291 
292         cm().unregisterNetworkCallback(mMobileNetworkCallback);
293         mMobileNetworkCallback = null;
294     }
295 
296     // So many TODOs here, but chief among them is: make this functionality an
297     // integral part of this class such that whenever a higher priority network
298     // becomes available and useful we (a) file a request to keep it up as
299     // necessary and (b) change all upstream tracking state accordingly (by
300     // passing LinkProperties up to Tethering).
301     /**
302      * Select the first available network from |perferredTypes|.
303      */
selectPreferredUpstreamType(Iterable<Integer> preferredTypes)304     public UpstreamNetworkState selectPreferredUpstreamType(Iterable<Integer> preferredTypes) {
305         final TypeStatePair typeStatePair = findFirstAvailableUpstreamByType(
306                 mNetworkMap.values(), preferredTypes, isCellularUpstreamPermitted());
307 
308         mLog.log("preferred upstream type: " + typeStatePair.type);
309 
310         switch (typeStatePair.type) {
311             case TYPE_MOBILE_DUN:
312             case TYPE_MOBILE_HIPRI:
313                 // Tethering just selected mobile upstream in spite of the default network being
314                 // not mobile. This can happen because of the priority list.
315                 // Notify EntitlementManager to check permission for using mobile upstream.
316                 if (!mIsDefaultCellularUpstream) {
317                     mEntitlementMgr.maybeRunProvisioning();
318                 }
319                 break;
320         }
321 
322         return typeStatePair.ns;
323     }
324 
325     /**
326      * Get current preferred upstream network. If default network is cellular and DUN is required,
327      * preferred upstream would be DUN otherwise preferred upstream is the same as default network.
328      * Returns null if no current upstream is available.
329      */
getCurrentPreferredUpstream()330     public UpstreamNetworkState getCurrentPreferredUpstream() {
331         final UpstreamNetworkState dfltState = (mDefaultInternetNetwork != null)
332                 ? mNetworkMap.get(mDefaultInternetNetwork)
333                 : null;
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             if (mCallbackType == CALLBACK_DEFAULT_INTERNET) {
537                 updateLinkProperties(network, newLp);
538                 // When the default network callback calls onLinkPropertiesChanged, it means that
539                 // all the network information for the default network is known (because
540                 // onLinkPropertiesChanged is called after onAvailable and onCapabilitiesChanged).
541                 // Inform tethering that the default network might have changed.
542                 maybeHandleNetworkSwitch(network);
543                 return;
544             }
545 
546             handleLinkProp(network, newLp);
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 
665     /**
666      * Given a legacy type (TYPE_WIFI, ...) returns the corresponding NetworkCapabilities instance.
667      * This function is used for deprecated legacy type and be disabled by default.
668      */
669     @VisibleForTesting
networkCapabilitiesForType(int type)670     public static NetworkCapabilities networkCapabilitiesForType(int type) {
671         final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
672 
673         // Map from type to transports.
674         final int notFound = -1;
675         final int transport = sLegacyTypeToTransport.get(type, notFound);
676         if (transport == notFound) {
677             throw new IllegalArgumentException("unknown legacy type: " + type);
678         }
679         builder.addTransportType(transport);
680 
681         if (type == TYPE_MOBILE_DUN) {
682             builder.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
683             // DUN is restricted network, see NetworkCapabilities#FORCE_RESTRICTED_CAPABILITIES.
684             builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
685         } else {
686             builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
687         }
688         return builder.build();
689     }
690 }
691