• 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.server.connectivity.tethering;
18 
19 import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
20 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
21 import static android.net.ConnectivityManager.TYPE_NONE;
22 import static android.net.ConnectivityManager.getNetworkTypeName;
23 import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
24 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
25 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
26 
27 import android.content.Context;
28 import android.net.ConnectivityManager;
29 import android.net.ConnectivityManager.NetworkCallback;
30 import android.net.IpPrefix;
31 import android.net.LinkProperties;
32 import android.net.Network;
33 import android.net.NetworkCapabilities;
34 import android.net.NetworkRequest;
35 import android.net.NetworkState;
36 import android.net.util.PrefixUtils;
37 import android.net.util.SharedLog;
38 import android.os.Handler;
39 import android.os.Process;
40 import android.util.Log;
41 
42 import com.android.internal.annotations.VisibleForTesting;
43 import com.android.internal.util.StateMachine;
44 
45 import java.util.HashMap;
46 import java.util.HashSet;
47 import java.util.Set;
48 
49 
50 /**
51  * A class to centralize all the network and link properties information
52  * pertaining to the current and any potential upstream network.
53  *
54  * The owner of UNM gets it to register network callbacks by calling the
55  * following methods :
56  * Calling #startTrackDefaultNetwork() to track the system default network.
57  * Calling #startObserveAllNetworks() to observe all networks. Listening all
58  * networks is necessary while the expression of preferred upstreams remains
59  * a list of legacy connectivity types.  In future, this can be revisited.
60  * Calling #registerMobileNetworkRequest() to bring up mobile DUN/HIPRI network.
61  *
62  * The methods and data members of this class are only to be accessed and
63  * modified from the tethering master state machine thread. Any other
64  * access semantics would necessitate the addition of locking.
65  *
66  * TODO: Move upstream selection logic here.
67  *
68  * All callback methods are run on the same thread as the specified target
69  * state machine.  This class does not require locking when accessed from this
70  * thread.  Access from other threads is not advised.
71  *
72  * @hide
73  */
74 public class UpstreamNetworkMonitor {
75     private static final String TAG = UpstreamNetworkMonitor.class.getSimpleName();
76     private static final boolean DBG = false;
77     private static final boolean VDBG = false;
78 
79     public static final int EVENT_ON_CAPABILITIES   = 1;
80     public static final int EVENT_ON_LINKPROPERTIES = 2;
81     public static final int EVENT_ON_LOST           = 3;
82     public static final int NOTIFY_LOCAL_PREFIXES   = 10;
83 
84     private static final int CALLBACK_LISTEN_ALL = 1;
85     private static final int CALLBACK_DEFAULT_INTERNET = 2;
86     private static final int CALLBACK_MOBILE_REQUEST = 3;
87 
88     private final Context mContext;
89     private final SharedLog mLog;
90     private final StateMachine mTarget;
91     private final Handler mHandler;
92     private final int mWhat;
93     private final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>();
94     private HashSet<IpPrefix> mLocalPrefixes;
95     private ConnectivityManager mCM;
96     private EntitlementManager mEntitlementMgr;
97     private NetworkCallback mListenAllCallback;
98     private NetworkCallback mDefaultNetworkCallback;
99     private NetworkCallback mMobileNetworkCallback;
100     private boolean mDunRequired;
101     // Whether the current default upstream is mobile or not.
102     private boolean mIsDefaultCellularUpstream;
103     // The current system default network (not really used yet).
104     private Network mDefaultInternetNetwork;
105     // The current upstream network used for tethering.
106     private Network mTetheringUpstreamNetwork;
107 
UpstreamNetworkMonitor(Context ctx, StateMachine tgt, SharedLog log, int what)108     public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, SharedLog log, int what) {
109         mContext = ctx;
110         mTarget = tgt;
111         mHandler = mTarget.getHandler();
112         mLog = log.forSubComponent(TAG);
113         mWhat = what;
114         mLocalPrefixes = new HashSet<>();
115         mIsDefaultCellularUpstream = false;
116     }
117 
118     @VisibleForTesting
UpstreamNetworkMonitor( ConnectivityManager cm, StateMachine tgt, SharedLog log, int what)119     public UpstreamNetworkMonitor(
120             ConnectivityManager cm, StateMachine tgt, SharedLog log, int what) {
121         this((Context) null, tgt, log, what);
122         mCM = cm;
123     }
124 
125     /**
126      * Tracking the system default network. This method should be called when system is ready.
127      *
128      * @param defaultNetworkRequest should be the same as ConnectivityService default request
129      * @param entitle a EntitlementManager object to communicate between EntitlementManager and
130      * UpstreamNetworkMonitor
131      */
startTrackDefaultNetwork(NetworkRequest defaultNetworkRequest, EntitlementManager entitle)132     public void startTrackDefaultNetwork(NetworkRequest defaultNetworkRequest,
133             EntitlementManager entitle) {
134         // This is not really a "request", just a way of tracking the system default network.
135         // It's guaranteed not to actually bring up any networks because it's the same request
136         // as the ConnectivityService default request, and thus shares fate with it. We can't
137         // use registerDefaultNetworkCallback because it will not track the system default
138         // network if there is a VPN that applies to our UID.
139         if (mDefaultNetworkCallback == null) {
140             final NetworkRequest trackDefaultRequest = new NetworkRequest(defaultNetworkRequest);
141             mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_DEFAULT_INTERNET);
142             cm().requestNetwork(trackDefaultRequest, mDefaultNetworkCallback, mHandler);
143         }
144         if (mEntitlementMgr == null) {
145             mEntitlementMgr = entitle;
146         }
147     }
148 
startObserveAllNetworks()149     public void startObserveAllNetworks() {
150         stop();
151 
152         final NetworkRequest listenAllRequest = new NetworkRequest.Builder()
153                 .clearCapabilities().build();
154         mListenAllCallback = new UpstreamNetworkCallback(CALLBACK_LISTEN_ALL);
155         cm().registerNetworkCallback(listenAllRequest, mListenAllCallback, mHandler);
156     }
157 
stop()158     public void stop() {
159         releaseMobileNetworkRequest();
160 
161         releaseCallback(mListenAllCallback);
162         mListenAllCallback = null;
163 
164         mTetheringUpstreamNetwork = null;
165         mNetworkMap.clear();
166     }
167 
updateMobileRequiresDun(boolean dunRequired)168     public void updateMobileRequiresDun(boolean dunRequired) {
169         final boolean valueChanged = (mDunRequired != dunRequired);
170         mDunRequired = dunRequired;
171         if (valueChanged && mobileNetworkRequested()) {
172             releaseMobileNetworkRequest();
173             registerMobileNetworkRequest();
174         }
175     }
176 
mobileNetworkRequested()177     public boolean mobileNetworkRequested() {
178         return (mMobileNetworkCallback != null);
179     }
180 
registerMobileNetworkRequest()181     public void registerMobileNetworkRequest() {
182         if (!isCellularUpstreamPermitted()) {
183             mLog.i("registerMobileNetworkRequest() is not permitted");
184             releaseMobileNetworkRequest();
185             return;
186         }
187         if (mMobileNetworkCallback != null) {
188             mLog.e("registerMobileNetworkRequest() already registered");
189             return;
190         }
191         // The following use of the legacy type system cannot be removed until
192         // after upstream selection no longer finds networks by legacy type.
193         // See also http://b/34364553 .
194         final int legacyType = mDunRequired ? TYPE_MOBILE_DUN : TYPE_MOBILE_HIPRI;
195 
196         final NetworkRequest mobileUpstreamRequest = new NetworkRequest.Builder()
197                 .setCapabilities(ConnectivityManager.networkCapabilitiesForType(legacyType))
198                 .build();
199 
200         // The existing default network and DUN callbacks will be notified.
201         // Therefore, to avoid duplicate notifications, we only register a no-op.
202         mMobileNetworkCallback = new UpstreamNetworkCallback(CALLBACK_MOBILE_REQUEST);
203 
204         // TODO: Change the timeout from 0 (no onUnavailable callback) to some
205         // moderate callback timeout. This might be useful for updating some UI.
206         // Additionally, we log a message to aid in any subsequent debugging.
207         mLog.i("requesting mobile upstream network: " + mobileUpstreamRequest);
208 
209         cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback, 0, legacyType, mHandler);
210     }
211 
releaseMobileNetworkRequest()212     public void releaseMobileNetworkRequest() {
213         if (mMobileNetworkCallback == null) return;
214 
215         cm().unregisterNetworkCallback(mMobileNetworkCallback);
216         mMobileNetworkCallback = null;
217     }
218 
219     // So many TODOs here, but chief among them is: make this functionality an
220     // integral part of this class such that whenever a higher priority network
221     // becomes available and useful we (a) file a request to keep it up as
222     // necessary and (b) change all upstream tracking state accordingly (by
223     // passing LinkProperties up to Tethering).
selectPreferredUpstreamType(Iterable<Integer> preferredTypes)224     public NetworkState selectPreferredUpstreamType(Iterable<Integer> preferredTypes) {
225         final TypeStatePair typeStatePair = findFirstAvailableUpstreamByType(
226                 mNetworkMap.values(), preferredTypes, isCellularUpstreamPermitted());
227 
228         mLog.log("preferred upstream type: " + getNetworkTypeName(typeStatePair.type));
229 
230         switch (typeStatePair.type) {
231             case TYPE_MOBILE_DUN:
232             case TYPE_MOBILE_HIPRI:
233                 // Tethering just selected mobile upstream in spite of the default network being
234                 // not mobile. This can happen because of the priority list.
235                 // Notify EntitlementManager to check permission for using mobile upstream.
236                 if (!mIsDefaultCellularUpstream) {
237                     mEntitlementMgr.maybeRunProvisioning();
238                 }
239                 // If we're on DUN, put our own grab on it.
240                 registerMobileNetworkRequest();
241                 break;
242             case TYPE_NONE:
243                 // If we found NONE and mobile upstream is permitted we don't want to do this
244                 // as we want any previous requests to keep trying to bring up something we can use.
245                 if (!isCellularUpstreamPermitted()) releaseMobileNetworkRequest();
246                 break;
247             default:
248                 // If we've found an active upstream connection that's not DUN/HIPRI
249                 // we should stop any outstanding DUN/HIPRI requests.
250                 releaseMobileNetworkRequest();
251                 break;
252         }
253 
254         return typeStatePair.ns;
255     }
256 
257     // Returns null if no current upstream available.
getCurrentPreferredUpstream()258     public NetworkState getCurrentPreferredUpstream() {
259         final NetworkState dfltState = (mDefaultInternetNetwork != null)
260                 ? mNetworkMap.get(mDefaultInternetNetwork)
261                 : null;
262         if (isNetworkUsableAndNotCellular(dfltState)) return dfltState;
263 
264         if (!isCellularUpstreamPermitted()) return null;
265 
266         if (!mDunRequired) return dfltState;
267 
268         // Find a DUN network. Note that code in Tethering causes a DUN request
269         // to be filed, but this might be moved into this class in future.
270         return findFirstDunNetwork(mNetworkMap.values());
271     }
272 
setCurrentUpstream(Network upstream)273     public void setCurrentUpstream(Network upstream) {
274         mTetheringUpstreamNetwork = upstream;
275     }
276 
getLocalPrefixes()277     public Set<IpPrefix> getLocalPrefixes() {
278         return (Set<IpPrefix>) mLocalPrefixes.clone();
279     }
280 
isCellularUpstreamPermitted()281     private boolean isCellularUpstreamPermitted() {
282         if (mEntitlementMgr != null) {
283             return mEntitlementMgr.isCellularUpstreamPermitted();
284         } else {
285             // This flow should only happens in testing.
286             return true;
287         }
288     }
289 
handleAvailable(Network network)290     private void handleAvailable(Network network) {
291         if (mNetworkMap.containsKey(network)) return;
292 
293         if (VDBG) Log.d(TAG, "onAvailable for " + network);
294         mNetworkMap.put(network, new NetworkState(null, null, null, network, null, null));
295     }
296 
handleNetCap(Network network, NetworkCapabilities newNc)297     private void handleNetCap(Network network, NetworkCapabilities newNc) {
298         final NetworkState prev = mNetworkMap.get(network);
299         if (prev == null || newNc.equals(prev.networkCapabilities)) {
300             // Ignore notifications about networks for which we have not yet
301             // received onAvailable() (should never happen) and any duplicate
302             // notifications (e.g. matching more than one of our callbacks).
303             return;
304         }
305 
306         if (VDBG) {
307             Log.d(TAG, String.format("EVENT_ON_CAPABILITIES for %s: %s",
308                     network, newNc));
309         }
310 
311         // Log changes in upstream network signal strength, if available.
312         if (network.equals(mTetheringUpstreamNetwork) && newNc.hasSignalStrength()) {
313             final int newSignal = newNc.getSignalStrength();
314             final String prevSignal = getSignalStrength(prev.networkCapabilities);
315             mLog.logf("upstream network signal strength: %s -> %s", prevSignal, newSignal);
316         }
317 
318         mNetworkMap.put(network, new NetworkState(
319                 null, prev.linkProperties, newNc, network, null, null));
320         // TODO: If sufficient information is available to select a more
321         // preferable upstream, do so now and notify the target.
322         notifyTarget(EVENT_ON_CAPABILITIES, network);
323     }
324 
handleLinkProp(Network network, LinkProperties newLp)325     private void handleLinkProp(Network network, LinkProperties newLp) {
326         final NetworkState prev = mNetworkMap.get(network);
327         if (prev == null || newLp.equals(prev.linkProperties)) {
328             // Ignore notifications about networks for which we have not yet
329             // received onAvailable() (should never happen) and any duplicate
330             // notifications (e.g. matching more than one of our callbacks).
331             return;
332         }
333 
334         if (VDBG) {
335             Log.d(TAG, String.format("EVENT_ON_LINKPROPERTIES for %s: %s",
336                     network, newLp));
337         }
338 
339         mNetworkMap.put(network, new NetworkState(
340                 null, newLp, prev.networkCapabilities, network, null, null));
341         // TODO: If sufficient information is available to select a more
342         // preferable upstream, do so now and notify the target.
343         notifyTarget(EVENT_ON_LINKPROPERTIES, network);
344     }
345 
handleSuspended(Network network)346     private void handleSuspended(Network network) {
347         if (!network.equals(mTetheringUpstreamNetwork)) return;
348         mLog.log("SUSPENDED current upstream: " + network);
349     }
350 
handleResumed(Network network)351     private void handleResumed(Network network) {
352         if (!network.equals(mTetheringUpstreamNetwork)) return;
353         mLog.log("RESUMED current upstream: " + network);
354     }
355 
handleLost(Network network)356     private void handleLost(Network network) {
357         // There are few TODOs within ConnectivityService's rematching code
358         // pertaining to spurious onLost() notifications.
359         //
360         // TODO: simplify this, probably if favor of code that:
361         //     - selects a new upstream if mTetheringUpstreamNetwork has
362         //       been lost (by any callback)
363         //     - deletes the entry from the map only when the LISTEN_ALL
364         //       callback gets notified.
365 
366         if (!mNetworkMap.containsKey(network)) {
367             // Ignore loss of networks about which we had not previously
368             // learned any information or for which we have already processed
369             // an onLost() notification.
370             return;
371         }
372 
373         if (VDBG) Log.d(TAG, "EVENT_ON_LOST for " + network);
374 
375         // TODO: If sufficient information is available to select a more
376         // preferable upstream, do so now and notify the target.  Likewise,
377         // if the current upstream network is gone, notify the target of the
378         // fact that we now have no upstream at all.
379         notifyTarget(EVENT_ON_LOST, mNetworkMap.remove(network));
380     }
381 
recomputeLocalPrefixes()382     private void recomputeLocalPrefixes() {
383         final HashSet<IpPrefix> localPrefixes = allLocalPrefixes(mNetworkMap.values());
384         if (!mLocalPrefixes.equals(localPrefixes)) {
385             mLocalPrefixes = localPrefixes;
386             notifyTarget(NOTIFY_LOCAL_PREFIXES, localPrefixes.clone());
387         }
388     }
389 
390     // Fetch (and cache) a ConnectivityManager only if and when we need one.
cm()391     private ConnectivityManager cm() {
392         if (mCM == null) {
393             // MUST call the String variant to be able to write unittests.
394             mCM = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
395         }
396         return mCM;
397     }
398 
399     /**
400      * A NetworkCallback class that handles information of interest directly
401      * in the thread on which it is invoked. To avoid locking, this MUST be
402      * run on the same thread as the target state machine's handler.
403      */
404     private class UpstreamNetworkCallback extends NetworkCallback {
405         private final int mCallbackType;
406 
UpstreamNetworkCallback(int callbackType)407         UpstreamNetworkCallback(int callbackType) {
408             mCallbackType = callbackType;
409         }
410 
411         @Override
onAvailable(Network network)412         public void onAvailable(Network network) {
413             handleAvailable(network);
414         }
415 
416         @Override
onCapabilitiesChanged(Network network, NetworkCapabilities newNc)417         public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) {
418             if (mCallbackType == CALLBACK_DEFAULT_INTERNET) {
419                 mDefaultInternetNetwork = network;
420                 final boolean newIsCellular = isCellular(newNc);
421                 if (mIsDefaultCellularUpstream != newIsCellular) {
422                     mIsDefaultCellularUpstream = newIsCellular;
423                     mEntitlementMgr.notifyUpstream(newIsCellular);
424                 }
425                 return;
426             }
427 
428             handleNetCap(network, newNc);
429         }
430 
431         @Override
onLinkPropertiesChanged(Network network, LinkProperties newLp)432         public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
433             if (mCallbackType == CALLBACK_DEFAULT_INTERNET) return;
434 
435             handleLinkProp(network, newLp);
436             // Any non-LISTEN_ALL callback will necessarily concern a network that will
437             // also match the LISTEN_ALL callback by construction of the LISTEN_ALL callback.
438             // So it's not useful to do this work for non-LISTEN_ALL callbacks.
439             if (mCallbackType == CALLBACK_LISTEN_ALL) {
440                 recomputeLocalPrefixes();
441             }
442         }
443 
444         @Override
onNetworkSuspended(Network network)445         public void onNetworkSuspended(Network network) {
446             if (mCallbackType == CALLBACK_LISTEN_ALL) {
447                 handleSuspended(network);
448             }
449         }
450 
451         @Override
onNetworkResumed(Network network)452         public void onNetworkResumed(Network network) {
453             if (mCallbackType == CALLBACK_LISTEN_ALL) {
454                 handleResumed(network);
455             }
456         }
457 
458         @Override
onLost(Network network)459         public void onLost(Network network) {
460             if (mCallbackType == CALLBACK_DEFAULT_INTERNET) {
461                 mDefaultInternetNetwork = null;
462                 mIsDefaultCellularUpstream = false;
463                 mEntitlementMgr.notifyUpstream(false);
464                 return;
465             }
466 
467             handleLost(network);
468             // Any non-LISTEN_ALL callback will necessarily concern a network that will
469             // also match the LISTEN_ALL callback by construction of the LISTEN_ALL callback.
470             // So it's not useful to do this work for non-LISTEN_ALL callbacks.
471             if (mCallbackType == CALLBACK_LISTEN_ALL) {
472                 recomputeLocalPrefixes();
473             }
474         }
475     }
476 
releaseCallback(NetworkCallback cb)477     private void releaseCallback(NetworkCallback cb) {
478         if (cb != null) cm().unregisterNetworkCallback(cb);
479     }
480 
notifyTarget(int which, Network network)481     private void notifyTarget(int which, Network network) {
482         notifyTarget(which, mNetworkMap.get(network));
483     }
484 
notifyTarget(int which, Object obj)485     private void notifyTarget(int which, Object obj) {
486         mTarget.sendMessage(mWhat, which, 0, obj);
487     }
488 
489     private static class TypeStatePair {
490         public int type = TYPE_NONE;
491         public NetworkState ns = null;
492     }
493 
findFirstAvailableUpstreamByType( Iterable<NetworkState> netStates, Iterable<Integer> preferredTypes, boolean isCellularUpstreamPermitted)494     private static TypeStatePair findFirstAvailableUpstreamByType(
495             Iterable<NetworkState> netStates, Iterable<Integer> preferredTypes,
496             boolean isCellularUpstreamPermitted) {
497         final TypeStatePair result = new TypeStatePair();
498 
499         for (int type : preferredTypes) {
500             NetworkCapabilities nc;
501             try {
502                 nc = ConnectivityManager.networkCapabilitiesForType(type);
503             } catch (IllegalArgumentException iae) {
504                 Log.e(TAG, "No NetworkCapabilities mapping for legacy type: " +
505                        ConnectivityManager.getNetworkTypeName(type));
506                 continue;
507             }
508             if (!isCellularUpstreamPermitted && isCellular(nc)) {
509                 continue;
510             }
511 
512             nc.setSingleUid(Process.myUid());
513 
514             for (NetworkState value : netStates) {
515                 if (!nc.satisfiedByNetworkCapabilities(value.networkCapabilities)) {
516                     continue;
517                 }
518 
519                 result.type = type;
520                 result.ns = value;
521                 return result;
522             }
523         }
524 
525         return result;
526     }
527 
allLocalPrefixes(Iterable<NetworkState> netStates)528     private static HashSet<IpPrefix> allLocalPrefixes(Iterable<NetworkState> netStates) {
529         final HashSet<IpPrefix> prefixSet = new HashSet<>();
530 
531         for (NetworkState ns : netStates) {
532             final LinkProperties lp = ns.linkProperties;
533             if (lp == null) continue;
534             prefixSet.addAll(PrefixUtils.localPrefixesFrom(lp));
535         }
536 
537         return prefixSet;
538     }
539 
getSignalStrength(NetworkCapabilities nc)540     private static String getSignalStrength(NetworkCapabilities nc) {
541         if (nc == null || !nc.hasSignalStrength()) return "unknown";
542         return Integer.toString(nc.getSignalStrength());
543     }
544 
isCellular(NetworkState ns)545     private static boolean isCellular(NetworkState ns) {
546         return (ns != null) && isCellular(ns.networkCapabilities);
547     }
548 
isCellular(NetworkCapabilities nc)549     private static boolean isCellular(NetworkCapabilities nc) {
550         return (nc != null) && nc.hasTransport(TRANSPORT_CELLULAR) &&
551                nc.hasCapability(NET_CAPABILITY_NOT_VPN);
552     }
553 
hasCapability(NetworkState ns, int netCap)554     private static boolean hasCapability(NetworkState ns, int netCap) {
555         return (ns != null) && (ns.networkCapabilities != null) &&
556                ns.networkCapabilities.hasCapability(netCap);
557     }
558 
isNetworkUsableAndNotCellular(NetworkState ns)559     private static boolean isNetworkUsableAndNotCellular(NetworkState ns) {
560         return (ns != null) && (ns.networkCapabilities != null) && (ns.linkProperties != null) &&
561                !isCellular(ns.networkCapabilities);
562     }
563 
findFirstDunNetwork(Iterable<NetworkState> netStates)564     private static NetworkState findFirstDunNetwork(Iterable<NetworkState> netStates) {
565         for (NetworkState ns : netStates) {
566             if (isCellular(ns) && hasCapability(ns, NET_CAPABILITY_DUN)) return ns;
567         }
568 
569         return null;
570     }
571 }
572