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