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