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