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