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