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