1 /* 2 * Copyright (C) 2020 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.vcn.routeselection; 18 19 import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY; 20 import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN; 21 import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED; 22 import static android.net.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; 23 import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener; 24 25 import static com.android.server.VcnManagementService.LOCAL_LOG; 26 import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiEntryRssiThreshold; 27 import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiExitRssiThreshold; 28 29 import android.annotation.NonNull; 30 import android.annotation.Nullable; 31 import android.annotation.TargetApi; 32 import android.net.ConnectivityManager; 33 import android.net.ConnectivityManager.NetworkCallback; 34 import android.net.IpSecTransform; 35 import android.net.LinkProperties; 36 import android.net.Network; 37 import android.net.NetworkCapabilities; 38 import android.net.NetworkRequest; 39 import android.net.TelephonyNetworkSpecifier; 40 import android.net.vcn.VcnCellUnderlyingNetworkTemplate; 41 import android.net.vcn.VcnGatewayConnectionConfig; 42 import android.net.vcn.VcnUnderlyingNetworkTemplate; 43 import android.net.vcn.util.LogUtils; 44 import android.os.Build; 45 import android.os.Handler; 46 import android.os.ParcelUuid; 47 import android.telephony.TelephonyCallback; 48 import android.telephony.TelephonyManager; 49 import android.util.ArrayMap; 50 import android.util.ArraySet; 51 import android.util.IndentingPrintWriter; 52 import android.util.Slog; 53 54 import com.android.internal.annotations.VisibleForTesting; 55 import com.android.internal.annotations.VisibleForTesting.Visibility; 56 import com.android.modules.utils.HandlerExecutor; 57 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; 58 import com.android.server.vcn.VcnContext; 59 import com.android.server.vcn.routeselection.UnderlyingNetworkEvaluator.NetworkEvaluatorCallback; 60 61 import java.util.ArrayList; 62 import java.util.Collections; 63 import java.util.List; 64 import java.util.Map; 65 import java.util.Objects; 66 import java.util.Set; 67 import java.util.TreeSet; 68 69 /** 70 * Tracks a set of Networks underpinning a VcnGatewayConnection. 71 * 72 * <p>A single UnderlyingNetworkController is built to serve a SINGLE VCN Gateway Connection, and 73 * MUST be torn down with the VcnGatewayConnection in order to ensure underlying networks are 74 * allowed to be reaped. 75 * 76 * @hide 77 */ 78 @TargetApi(Build.VERSION_CODES.BAKLAVA) 79 public class UnderlyingNetworkController { 80 @NonNull private static final String TAG = UnderlyingNetworkController.class.getSimpleName(); 81 82 @NonNull private final VcnContext mVcnContext; 83 @NonNull private final VcnGatewayConnectionConfig mConnectionConfig; 84 @NonNull private final ParcelUuid mSubscriptionGroup; 85 @NonNull private final UnderlyingNetworkControllerCallback mCb; 86 @NonNull private final Dependencies mDeps; 87 @NonNull private final Handler mHandler; 88 @NonNull private final ConnectivityManager mConnectivityManager; 89 @NonNull private final TelephonyCallback mActiveDataSubIdListener = 90 new VcnActiveDataSubscriptionIdListener(); 91 92 private final Map<Network, UnderlyingNetworkEvaluator> mUnderlyingNetworkRecords = 93 new ArrayMap<>(); 94 95 @NonNull private final List<NetworkCallback> mCellBringupCallbacks = new ArrayList<>(); 96 @Nullable private NetworkCallback mWifiBringupCallback; 97 @Nullable private NetworkCallback mWifiEntryRssiThresholdCallback; 98 @Nullable private NetworkCallback mWifiExitRssiThresholdCallback; 99 @Nullable private UnderlyingNetworkListener mRouteSelectionCallback; 100 101 @NonNull private TelephonySubscriptionSnapshot mLastSnapshot; 102 @Nullable private PersistableBundleWrapper mCarrierConfig; 103 private boolean mIsQuitting = false; 104 105 @Nullable private UnderlyingNetworkRecord mCurrentRecord; 106 @Nullable private UnderlyingNetworkRecord.Builder mRecordInProgress; 107 UnderlyingNetworkController( @onNull VcnContext vcnContext, @NonNull VcnGatewayConnectionConfig connectionConfig, @NonNull ParcelUuid subscriptionGroup, @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull UnderlyingNetworkControllerCallback cb)108 public UnderlyingNetworkController( 109 @NonNull VcnContext vcnContext, 110 @NonNull VcnGatewayConnectionConfig connectionConfig, 111 @NonNull ParcelUuid subscriptionGroup, 112 @NonNull TelephonySubscriptionSnapshot snapshot, 113 @NonNull UnderlyingNetworkControllerCallback cb) { 114 this(vcnContext, connectionConfig, subscriptionGroup, snapshot, cb, new Dependencies()); 115 } 116 117 @VisibleForTesting(visibility = Visibility.PRIVATE) UnderlyingNetworkController( @onNull VcnContext vcnContext, @NonNull VcnGatewayConnectionConfig connectionConfig, @NonNull ParcelUuid subscriptionGroup, @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull UnderlyingNetworkControllerCallback cb, @NonNull Dependencies deps)118 UnderlyingNetworkController( 119 @NonNull VcnContext vcnContext, 120 @NonNull VcnGatewayConnectionConfig connectionConfig, 121 @NonNull ParcelUuid subscriptionGroup, 122 @NonNull TelephonySubscriptionSnapshot snapshot, 123 @NonNull UnderlyingNetworkControllerCallback cb, 124 @NonNull Dependencies deps) { 125 mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext"); 126 mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig"); 127 mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup"); 128 mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot"); 129 mCb = Objects.requireNonNull(cb, "Missing cb"); 130 mDeps = Objects.requireNonNull(deps, "Missing deps"); 131 132 mHandler = new Handler(mVcnContext.getLooper()); 133 134 mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class); 135 mVcnContext 136 .getContext() 137 .getSystemService(TelephonyManager.class) 138 .registerTelephonyCallback(new HandlerExecutor(mHandler), mActiveDataSubIdListener); 139 140 mCarrierConfig = mLastSnapshot.getCarrierConfigForSubGrp(mSubscriptionGroup); 141 142 registerOrUpdateNetworkRequests(); 143 } 144 145 private static class CapabilityMatchCriteria { 146 public final int capability; 147 public final int matchCriteria; 148 CapabilityMatchCriteria(int capability, int matchCriteria)149 CapabilityMatchCriteria(int capability, int matchCriteria) { 150 this.capability = capability; 151 this.matchCriteria = matchCriteria; 152 } 153 154 @Override hashCode()155 public int hashCode() { 156 return Objects.hash(capability, matchCriteria); 157 } 158 159 @Override equals(@ullable Object other)160 public boolean equals(@Nullable Object other) { 161 if (!(other instanceof CapabilityMatchCriteria)) { 162 return false; 163 } 164 165 final CapabilityMatchCriteria rhs = (CapabilityMatchCriteria) other; 166 return capability == rhs.capability && matchCriteria == rhs.matchCriteria; 167 } 168 } 169 dedupAndGetCapRequirementsForCell( VcnGatewayConnectionConfig connectionConfig)170 private static Set<Set<CapabilityMatchCriteria>> dedupAndGetCapRequirementsForCell( 171 VcnGatewayConnectionConfig connectionConfig) { 172 final Set<Set<CapabilityMatchCriteria>> dedupedCapsMatchSets = new ArraySet<>(); 173 174 for (VcnUnderlyingNetworkTemplate template : 175 connectionConfig.getVcnUnderlyingNetworkPriorities()) { 176 if (template instanceof VcnCellUnderlyingNetworkTemplate) { 177 final Set<CapabilityMatchCriteria> capsMatchSet = new ArraySet<>(); 178 179 for (Map.Entry<Integer, Integer> entry : 180 ((VcnCellUnderlyingNetworkTemplate) template) 181 .getCapabilitiesMatchCriteria() 182 .entrySet()) { 183 184 final int capability = entry.getKey(); 185 final int matchCriteria = entry.getValue(); 186 if (matchCriteria != MATCH_ANY) { 187 capsMatchSet.add(new CapabilityMatchCriteria(capability, matchCriteria)); 188 } 189 } 190 191 dedupedCapsMatchSets.add(capsMatchSet); 192 } 193 } 194 195 dedupedCapsMatchSets.add( 196 Collections.singleton( 197 new CapabilityMatchCriteria( 198 NetworkCapabilities.NET_CAPABILITY_INTERNET, MATCH_REQUIRED))); 199 return dedupedCapsMatchSets; 200 } 201 registerOrUpdateNetworkRequests()202 private void registerOrUpdateNetworkRequests() { 203 NetworkCallback oldRouteSelectionCallback = mRouteSelectionCallback; 204 NetworkCallback oldWifiCallback = mWifiBringupCallback; 205 NetworkCallback oldWifiEntryRssiThresholdCallback = mWifiEntryRssiThresholdCallback; 206 NetworkCallback oldWifiExitRssiThresholdCallback = mWifiExitRssiThresholdCallback; 207 List<NetworkCallback> oldCellCallbacks = new ArrayList<>(mCellBringupCallbacks); 208 mCellBringupCallbacks.clear(); 209 210 for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) { 211 evaluator.close(); 212 } 213 214 mUnderlyingNetworkRecords.clear(); 215 216 // Register new callbacks. Make-before-break; always register new callbacks before removal 217 // of old callbacks 218 if (!mIsQuitting) { 219 mRouteSelectionCallback = new UnderlyingNetworkListener(); 220 mConnectivityManager.registerNetworkCallback( 221 getRouteSelectionRequest(), mRouteSelectionCallback, mHandler); 222 223 mWifiEntryRssiThresholdCallback = new NetworkBringupCallback(); 224 mConnectivityManager.registerNetworkCallback( 225 getWifiEntryRssiThresholdNetworkRequest(), 226 mWifiEntryRssiThresholdCallback, 227 mHandler); 228 229 mWifiExitRssiThresholdCallback = new NetworkBringupCallback(); 230 mConnectivityManager.registerNetworkCallback( 231 getWifiExitRssiThresholdNetworkRequest(), 232 mWifiExitRssiThresholdCallback, 233 mHandler); 234 235 mWifiBringupCallback = new NetworkBringupCallback(); 236 mConnectivityManager.requestBackgroundNetwork( 237 getWifiNetworkRequest(), mWifiBringupCallback, mHandler); 238 239 for (final int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) { 240 for (Set<CapabilityMatchCriteria> capsMatchCriteria : 241 dedupAndGetCapRequirementsForCell(mConnectionConfig)) { 242 final NetworkBringupCallback cb = new NetworkBringupCallback(); 243 mCellBringupCallbacks.add(cb); 244 245 mConnectivityManager.requestBackgroundNetwork( 246 getCellNetworkRequestForSubId(subId, capsMatchCriteria), cb, mHandler); 247 } 248 } 249 } else { 250 mRouteSelectionCallback = null; 251 mWifiBringupCallback = null; 252 mWifiEntryRssiThresholdCallback = null; 253 mWifiExitRssiThresholdCallback = null; 254 // mCellBringupCallbacks already cleared above. 255 } 256 257 // Unregister old callbacks (as necessary) 258 if (oldRouteSelectionCallback != null) { 259 mConnectivityManager.unregisterNetworkCallback(oldRouteSelectionCallback); 260 } 261 if (oldWifiCallback != null) { 262 mConnectivityManager.unregisterNetworkCallback(oldWifiCallback); 263 } 264 if (oldWifiEntryRssiThresholdCallback != null) { 265 mConnectivityManager.unregisterNetworkCallback(oldWifiEntryRssiThresholdCallback); 266 } 267 if (oldWifiExitRssiThresholdCallback != null) { 268 mConnectivityManager.unregisterNetworkCallback(oldWifiExitRssiThresholdCallback); 269 } 270 for (NetworkCallback cellBringupCallback : oldCellCallbacks) { 271 mConnectivityManager.unregisterNetworkCallback(cellBringupCallback); 272 } 273 } 274 275 /** 276 * Builds the Route selection request 277 * 278 * <p>This request is guaranteed to select carrier-owned, non-VCN underlying networks by virtue 279 * of a populated set of subIds as expressed in NetworkCapabilities#getSubscriptionIds(). Only 280 * carrier owned networks may be selected, as the request specifies only subIds in the VCN's 281 * subscription group, while the VCN networks are excluded by virtue of not having subIds set on 282 * the VCN-exposed networks. 283 * 284 * <p>If the VCN that this UnderlyingNetworkController belongs to is in test-mode, this will 285 * return a NetworkRequest that only matches Test Networks. 286 */ getRouteSelectionRequest()287 private NetworkRequest getRouteSelectionRequest() { 288 if (mVcnContext.isInTestMode()) { 289 return getTestNetworkRequest(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)); 290 } 291 292 return getBaseNetworkRequestBuilder() 293 .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) 294 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED) 295 .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) 296 .build(); 297 } 298 getBaseWifiNetworkRequestBuilder()299 private NetworkRequest.Builder getBaseWifiNetworkRequestBuilder() { 300 return getBaseNetworkRequestBuilder() 301 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) 302 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 303 .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)); 304 } 305 306 /** 307 * Builds the WiFi bringup request 308 * 309 * <p>This request is built specifically to match only carrier-owned WiFi networks, but is also 310 * built to ONLY keep Carrier WiFi Networks alive (but never bring them up). This is a result of 311 * the WifiNetworkFactory not advertising a list of subIds, and therefore not accepting this 312 * request. As such, it will bind to a Carrier WiFi Network that has already been brought up, 313 * but will NEVER bring up a Carrier WiFi network itself. 314 */ getWifiNetworkRequest()315 private NetworkRequest getWifiNetworkRequest() { 316 return getBaseWifiNetworkRequestBuilder().build(); 317 } 318 319 /** 320 * Builds the WiFi entry threshold signal strength request 321 * 322 * <p>This request ensures that WiFi reports the crossing of the wifi entry RSSI threshold. 323 * Without this request, WiFi rate-limits, and reports signal strength changes at too slow a 324 * pace to effectively select a short-lived WiFi offload network. 325 */ getWifiEntryRssiThresholdNetworkRequest()326 private NetworkRequest getWifiEntryRssiThresholdNetworkRequest() { 327 return getBaseWifiNetworkRequestBuilder() 328 // Ensure wifi updates signal strengths when crossing this threshold. 329 .setSignalStrength(getWifiEntryRssiThreshold(mCarrierConfig)) 330 .build(); 331 } 332 333 /** 334 * Builds the WiFi exit threshold signal strength request 335 * 336 * <p>This request ensures that WiFi reports the crossing of the wifi exit RSSI threshold. 337 * Without this request, WiFi rate-limits, and reports signal strength changes at too slow a 338 * pace to effectively select away from a failing WiFi network. 339 */ getWifiExitRssiThresholdNetworkRequest()340 private NetworkRequest getWifiExitRssiThresholdNetworkRequest() { 341 return getBaseWifiNetworkRequestBuilder() 342 // Ensure wifi updates signal strengths when crossing this threshold. 343 .setSignalStrength(getWifiExitRssiThreshold(mCarrierConfig)) 344 .build(); 345 } 346 347 /** 348 * Builds a Cellular bringup request for a given subId 349 * 350 * <p>This request is filed in order to ensure that the Telephony stack always has a 351 * NetworkRequest to bring up a VCN underlying cellular network. It is required in order to 352 * ensure that even when a VCN (appears as Cellular) satisfies the default request, Telephony 353 * will bring up additional underlying Cellular networks. 354 * 355 * <p>Since this request MUST make it to the TelephonyNetworkFactory, subIds are not specified 356 * in the NetworkCapabilities, but rather in the TelephonyNetworkSpecifier. 357 */ getCellNetworkRequestForSubId( int subId, Set<CapabilityMatchCriteria> capsMatchCriteria)358 private NetworkRequest getCellNetworkRequestForSubId( 359 int subId, Set<CapabilityMatchCriteria> capsMatchCriteria) { 360 final NetworkRequest.Builder nrBuilder = 361 getBaseNetworkRequestBuilder() 362 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) 363 .setNetworkSpecifier( 364 new TelephonyNetworkSpecifier.Builder() 365 .setSubscriptionId(subId) 366 .build()); 367 368 for (CapabilityMatchCriteria capMatchCriteria : capsMatchCriteria) { 369 final int cap = capMatchCriteria.capability; 370 final int matchCriteria = capMatchCriteria.matchCriteria; 371 372 if (matchCriteria == MATCH_REQUIRED) { 373 nrBuilder.addCapability(cap); 374 } else if (matchCriteria == MATCH_FORBIDDEN) { 375 nrBuilder.addForbiddenCapability(cap); 376 } 377 } 378 379 return nrBuilder.build(); 380 } 381 382 /** 383 * Builds and returns a NetworkRequest builder common to all Underlying Network requests 384 */ getBaseNetworkRequestBuilder()385 private NetworkRequest.Builder getBaseNetworkRequestBuilder() { 386 return new NetworkRequest.Builder() 387 .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) 388 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) 389 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); 390 } 391 392 /** Builds and returns a NetworkRequest for the given subIds to match Test Networks. */ getTestNetworkRequest(@onNull Set<Integer> subIds)393 private NetworkRequest getTestNetworkRequest(@NonNull Set<Integer> subIds) { 394 return new NetworkRequest.Builder() 395 .clearCapabilities() 396 .addTransportType(NetworkCapabilities.TRANSPORT_TEST) 397 .setSubscriptionIds(subIds) 398 .build(); 399 } 400 401 /** 402 * Update this UnderlyingNetworkController's TelephonySubscriptionSnapshot. 403 * 404 * <p>Updating the TelephonySubscriptionSnapshot will cause this UnderlyingNetworkController to 405 * reevaluate its NetworkBringupCallbacks. This may result in NetworkRequests being registered 406 * or unregistered if the subIds mapped to the this Tracker's SubscriptionGroup change. 407 */ updateSubscriptionSnapshot(@onNull TelephonySubscriptionSnapshot newSnapshot)408 public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot newSnapshot) { 409 Objects.requireNonNull(newSnapshot, "Missing newSnapshot"); 410 411 final TelephonySubscriptionSnapshot oldSnapshot = mLastSnapshot; 412 mLastSnapshot = newSnapshot; 413 414 // Update carrier config 415 mCarrierConfig = mLastSnapshot.getCarrierConfigForSubGrp(mSubscriptionGroup); 416 417 // Make sure all evaluators use the same updated TelephonySubscriptionSnapshot and carrier 418 // config to calculate their cached priority classes. For simplicity, the 419 // UnderlyingNetworkController does not listen for changes in VCN-related carrier config 420 // keys, and changes are applied at restart of the VcnGatewayConnection 421 for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) { 422 evaluator.reevaluate( 423 mConnectionConfig.getVcnUnderlyingNetworkPriorities(), 424 mSubscriptionGroup, 425 mLastSnapshot, 426 mCarrierConfig); 427 } 428 429 // Only trigger re-registration if subIds in this group have changed 430 if (oldSnapshot 431 .getAllSubIdsInGroup(mSubscriptionGroup) 432 .equals(newSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))) { 433 reevaluateNetworks(); 434 return; 435 } 436 registerOrUpdateNetworkRequests(); 437 } 438 439 /** 440 * Pass the IpSecTransform of the VCN to UnderlyingNetworkController for metric monitoring 441 * 442 * <p>Caller MUST call it when IpSecTransforms have been created for VCN creation or migration 443 */ updateInboundTransform( @onNull UnderlyingNetworkRecord currentNetwork, @NonNull IpSecTransform transform)444 public void updateInboundTransform( 445 @NonNull UnderlyingNetworkRecord currentNetwork, @NonNull IpSecTransform transform) { 446 Objects.requireNonNull(currentNetwork, "currentNetwork is null"); 447 Objects.requireNonNull(transform, "transform is null"); 448 449 if (mCurrentRecord == null 450 || mRouteSelectionCallback == null 451 || !Objects.equals(currentNetwork.network, mCurrentRecord.network)) { 452 // The caller (VcnGatewayConnection) is out-of-dated. Ignore this call. 453 return; 454 } 455 456 mUnderlyingNetworkRecords.get(mCurrentRecord.network).setInboundTransform(transform); 457 } 458 459 /** Tears down this Tracker, and releases all underlying network requests. */ teardown()460 public void teardown() { 461 mVcnContext.ensureRunningOnLooperThread(); 462 mIsQuitting = true; 463 464 // Will unregister all existing callbacks, but not register new ones due to quitting flag. 465 registerOrUpdateNetworkRequests(); 466 467 mVcnContext 468 .getContext() 469 .getSystemService(TelephonyManager.class) 470 .unregisterTelephonyCallback(mActiveDataSubIdListener); 471 } 472 getSortedUnderlyingNetworks()473 private TreeSet<UnderlyingNetworkEvaluator> getSortedUnderlyingNetworks() { 474 TreeSet<UnderlyingNetworkEvaluator> sorted = 475 new TreeSet<>(UnderlyingNetworkEvaluator.getComparator(mVcnContext)); 476 477 for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) { 478 if (evaluator.getPriorityClass() != NetworkPriorityClassifier.PRIORITY_INVALID) { 479 sorted.add(evaluator); 480 } 481 } 482 483 return sorted; 484 } 485 reevaluateNetworks()486 private void reevaluateNetworks() { 487 if (mIsQuitting || mRouteSelectionCallback == null) { 488 return; // UnderlyingNetworkController has quit. 489 } 490 491 TreeSet<UnderlyingNetworkEvaluator> sorted = getSortedUnderlyingNetworks(); 492 493 UnderlyingNetworkEvaluator candidateEvaluator = sorted.isEmpty() ? null : sorted.first(); 494 UnderlyingNetworkRecord candidate = 495 candidateEvaluator == null ? null : candidateEvaluator.getNetworkRecord(); 496 if (Objects.equals(mCurrentRecord, candidate)) { 497 return; 498 } 499 500 String allNetworkPriorities = ""; 501 for (UnderlyingNetworkEvaluator recordEvaluator : sorted) { 502 if (!allNetworkPriorities.isEmpty()) { 503 allNetworkPriorities += ", "; 504 } 505 allNetworkPriorities += 506 recordEvaluator.getNetwork() + ": " + recordEvaluator.getPriorityClass(); 507 } 508 509 if (!UnderlyingNetworkRecord.isSameNetwork(mCurrentRecord, candidate)) { 510 logInfo( 511 "Selected network changed to " 512 + (candidate == null ? null : candidate.network) 513 + ", selected from list: " 514 + allNetworkPriorities); 515 } 516 517 mCurrentRecord = candidate; 518 mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord); 519 520 // Need to update all evaluators to ensure the previously selected one is unselected 521 for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) { 522 evaluator.setIsSelected( 523 candidateEvaluator == evaluator, 524 mConnectionConfig.getVcnUnderlyingNetworkPriorities(), 525 mSubscriptionGroup, 526 mLastSnapshot, 527 mCarrierConfig); 528 } 529 } 530 531 /** 532 * NetworkBringupCallback is used to keep background, VCN-managed Networks from being reaped. 533 * 534 * <p>NetworkBringupCallback only exists to prevent matching (VCN-managed) Networks from being 535 * reaped, and no action is taken on any events firing. 536 */ 537 @VisibleForTesting 538 class NetworkBringupCallback extends NetworkCallback {} 539 540 /** 541 * RouteSelectionCallback is used to select the "best" underlying Network. 542 * 543 * <p>The "best" network is determined by ConnectivityService, which is treated as a source of 544 * truth. 545 */ 546 @VisibleForTesting 547 class UnderlyingNetworkListener extends NetworkCallback { UnderlyingNetworkListener()548 UnderlyingNetworkListener() { 549 super(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO); 550 } 551 552 @Override onAvailable(@onNull Network network)553 public void onAvailable(@NonNull Network network) { 554 mUnderlyingNetworkRecords.put( 555 network, 556 mDeps.newUnderlyingNetworkEvaluator( 557 mVcnContext, 558 network, 559 mConnectionConfig.getVcnUnderlyingNetworkPriorities(), 560 mSubscriptionGroup, 561 mLastSnapshot, 562 mCarrierConfig, 563 new NetworkEvaluatorCallbackImpl())); 564 } 565 566 @Override onLost(@onNull Network network)567 public void onLost(@NonNull Network network) { 568 mUnderlyingNetworkRecords.get(network).close(); 569 mUnderlyingNetworkRecords.remove(network); 570 571 reevaluateNetworks(); 572 } 573 574 @Override onCapabilitiesChanged( @onNull Network network, @NonNull NetworkCapabilities networkCapabilities)575 public void onCapabilitiesChanged( 576 @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) { 577 final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network); 578 if (evaluator == null) { 579 logWtf("Got capabilities change for unknown key: " + network); 580 return; 581 } 582 583 evaluator.setNetworkCapabilities( 584 networkCapabilities, 585 mConnectionConfig.getVcnUnderlyingNetworkPriorities(), 586 mSubscriptionGroup, 587 mLastSnapshot, 588 mCarrierConfig); 589 590 if (evaluator.isValid()) { 591 reevaluateNetworks(); 592 } 593 } 594 595 @Override onLinkPropertiesChanged( @onNull Network network, @NonNull LinkProperties linkProperties)596 public void onLinkPropertiesChanged( 597 @NonNull Network network, @NonNull LinkProperties linkProperties) { 598 final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network); 599 if (evaluator == null) { 600 logWtf("Got link properties change for unknown key: " + network); 601 return; 602 } 603 604 evaluator.setLinkProperties( 605 linkProperties, 606 mConnectionConfig.getVcnUnderlyingNetworkPriorities(), 607 mSubscriptionGroup, 608 mLastSnapshot, 609 mCarrierConfig); 610 611 if (evaluator.isValid()) { 612 reevaluateNetworks(); 613 } 614 } 615 616 @Override onBlockedStatusChanged(@onNull Network network, boolean isBlocked)617 public void onBlockedStatusChanged(@NonNull Network network, boolean isBlocked) { 618 final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network); 619 if (evaluator == null) { 620 logWtf("Got blocked status change for unknown key: " + network); 621 return; 622 } 623 624 evaluator.setIsBlocked( 625 isBlocked, 626 mConnectionConfig.getVcnUnderlyingNetworkPriorities(), 627 mSubscriptionGroup, 628 mLastSnapshot, 629 mCarrierConfig); 630 631 if (evaluator.isValid()) { 632 reevaluateNetworks(); 633 } 634 } 635 } 636 637 @VisibleForTesting 638 class NetworkEvaluatorCallbackImpl implements NetworkEvaluatorCallback { 639 @Override onEvaluationResultChanged()640 public void onEvaluationResultChanged() { 641 mVcnContext.ensureRunningOnLooperThread(); 642 reevaluateNetworks(); 643 } 644 } 645 getLogPrefix()646 private String getLogPrefix() { 647 return "(" 648 + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup) 649 + "-" 650 + mConnectionConfig.getGatewayConnectionName() 651 + "-" 652 + System.identityHashCode(this) 653 + ") "; 654 } 655 getTagLogPrefix()656 private String getTagLogPrefix() { 657 return "[ " + TAG + " " + getLogPrefix() + "]"; 658 } 659 logInfo(String msg)660 private void logInfo(String msg) { 661 Slog.i(TAG, getLogPrefix() + msg); 662 LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg); 663 } 664 logInfo(String msg, Throwable tr)665 private void logInfo(String msg, Throwable tr) { 666 Slog.i(TAG, getLogPrefix() + msg, tr); 667 LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg + tr); 668 } 669 logWtf(String msg)670 private void logWtf(String msg) { 671 Slog.wtf(TAG, msg); 672 LOCAL_LOG.log(TAG + "[WTF ] " + getTagLogPrefix() + msg); 673 } 674 logWtf(String msg, Throwable tr)675 private void logWtf(String msg, Throwable tr) { 676 Slog.wtf(TAG, msg, tr); 677 LOCAL_LOG.log(TAG + "[WTF ] " + getTagLogPrefix() + msg + tr); 678 } 679 680 /** Dumps the state of this record for logging and debugging purposes. */ dump(IndentingPrintWriter pw)681 public void dump(IndentingPrintWriter pw) { 682 pw.println("UnderlyingNetworkController:"); 683 pw.increaseIndent(); 684 685 pw.println("Carrier WiFi Entry Threshold: " + getWifiEntryRssiThreshold(mCarrierConfig)); 686 pw.println("Carrier WiFi Exit Threshold: " + getWifiExitRssiThreshold(mCarrierConfig)); 687 pw.println( 688 "Currently selected: " + (mCurrentRecord == null ? null : mCurrentRecord.network)); 689 690 pw.println("VcnUnderlyingNetworkTemplate list:"); 691 pw.increaseIndent(); 692 int index = 0; 693 for (VcnUnderlyingNetworkTemplate priority : 694 mConnectionConfig.getVcnUnderlyingNetworkPriorities()) { 695 pw.println("Priority index: " + index); 696 priority.dump(pw); 697 index++; 698 } 699 pw.decreaseIndent(); 700 pw.println(); 701 702 pw.println("Underlying networks:"); 703 pw.increaseIndent(); 704 if (mRouteSelectionCallback != null) { 705 for (UnderlyingNetworkEvaluator recordEvaluator : getSortedUnderlyingNetworks()) { 706 recordEvaluator.dump(pw); 707 } 708 } 709 pw.decreaseIndent(); 710 pw.println(); 711 712 pw.decreaseIndent(); 713 } 714 715 private class VcnActiveDataSubscriptionIdListener extends TelephonyCallback 716 implements ActiveDataSubscriptionIdListener { 717 @Override onActiveDataSubscriptionIdChanged(int subId)718 public void onActiveDataSubscriptionIdChanged(int subId) { 719 reevaluateNetworks(); 720 } 721 } 722 723 /** Callbacks for being notified of the changes in, or to the selected underlying network. */ 724 public interface UnderlyingNetworkControllerCallback { 725 /** 726 * Fired when a new underlying network is selected, or properties have changed. 727 * 728 * <p>This callback does NOT signal a mobility event. 729 * 730 * @param underlyingNetworkRecord The details of the new underlying network 731 */ onSelectedUnderlyingNetworkChanged( @ullable UnderlyingNetworkRecord underlyingNetworkRecord)732 void onSelectedUnderlyingNetworkChanged( 733 @Nullable UnderlyingNetworkRecord underlyingNetworkRecord); 734 } 735 736 @VisibleForTesting(visibility = Visibility.PRIVATE) 737 public static class Dependencies { newUnderlyingNetworkEvaluator( @onNull VcnContext vcnContext, @NonNull Network network, @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, @NonNull ParcelUuid subscriptionGroup, @NonNull TelephonySubscriptionSnapshot lastSnapshot, @Nullable PersistableBundleWrapper carrierConfig, @NonNull NetworkEvaluatorCallback evaluatorCallback)738 public UnderlyingNetworkEvaluator newUnderlyingNetworkEvaluator( 739 @NonNull VcnContext vcnContext, 740 @NonNull Network network, 741 @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, 742 @NonNull ParcelUuid subscriptionGroup, 743 @NonNull TelephonySubscriptionSnapshot lastSnapshot, 744 @Nullable PersistableBundleWrapper carrierConfig, 745 @NonNull NetworkEvaluatorCallback evaluatorCallback) { 746 return new UnderlyingNetworkEvaluator( 747 vcnContext, 748 network, 749 underlyingNetworkTemplates, 750 subscriptionGroup, 751 lastSnapshot, 752 carrierConfig, 753 evaluatorCallback); 754 } 755 } 756 } 757