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