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.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener; 23 24 import static com.android.server.VcnManagementService.LOCAL_LOG; 25 import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiEntryRssiThreshold; 26 import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiExitRssiThreshold; 27 import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; 28 29 import android.annotation.NonNull; 30 import android.annotation.Nullable; 31 import android.net.ConnectivityManager; 32 import android.net.ConnectivityManager.NetworkCallback; 33 import android.net.IpSecTransform; 34 import android.net.LinkProperties; 35 import android.net.Network; 36 import android.net.NetworkCapabilities; 37 import android.net.NetworkRequest; 38 import android.net.TelephonyNetworkSpecifier; 39 import android.net.vcn.VcnCellUnderlyingNetworkTemplate; 40 import android.net.vcn.VcnGatewayConnectionConfig; 41 import android.net.vcn.VcnUnderlyingNetworkTemplate; 42 import android.os.Handler; 43 import android.os.HandlerExecutor; 44 import android.os.ParcelUuid; 45 import android.telephony.TelephonyCallback; 46 import android.telephony.TelephonyManager; 47 import android.util.ArrayMap; 48 import android.util.ArraySet; 49 import android.util.Slog; 50 51 import com.android.internal.annotations.VisibleForTesting; 52 import com.android.internal.annotations.VisibleForTesting.Visibility; 53 import com.android.internal.util.IndentingPrintWriter; 54 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; 55 import com.android.server.vcn.VcnContext; 56 import com.android.server.vcn.routeselection.UnderlyingNetworkEvaluator.NetworkEvaluatorCallback; 57 import com.android.server.vcn.util.LogUtils; 58 59 import java.util.ArrayList; 60 import java.util.Collections; 61 import java.util.List; 62 import java.util.Map; 63 import java.util.Objects; 64 import java.util.Set; 65 import java.util.TreeSet; 66 67 /** 68 * Tracks a set of Networks underpinning a VcnGatewayConnection. 69 * 70 * <p>A single UnderlyingNetworkController is built to serve a SINGLE VCN Gateway Connection, and 71 * MUST be torn down with the VcnGatewayConnection in order to ensure underlying networks are 72 * allowed to be reaped. 73 * 74 * @hide 75 */ 76 public class UnderlyingNetworkController { 77 @NonNull private static final String TAG = UnderlyingNetworkController.class.getSimpleName(); 78 79 @NonNull private final VcnContext mVcnContext; 80 @NonNull private final VcnGatewayConnectionConfig mConnectionConfig; 81 @NonNull private final ParcelUuid mSubscriptionGroup; 82 @NonNull private final UnderlyingNetworkControllerCallback mCb; 83 @NonNull private final Dependencies mDeps; 84 @NonNull private final Handler mHandler; 85 @NonNull private final ConnectivityManager mConnectivityManager; 86 @NonNull private final TelephonyCallback mActiveDataSubIdListener = 87 new VcnActiveDataSubscriptionIdListener(); 88 89 private final Map<Network, UnderlyingNetworkEvaluator> mUnderlyingNetworkRecords = 90 new ArrayMap<>(); 91 92 @NonNull private final List<NetworkCallback> mCellBringupCallbacks = new ArrayList<>(); 93 @Nullable private NetworkCallback mWifiBringupCallback; 94 @Nullable private NetworkCallback mWifiEntryRssiThresholdCallback; 95 @Nullable private NetworkCallback mWifiExitRssiThresholdCallback; 96 @Nullable private UnderlyingNetworkListener mRouteSelectionCallback; 97 98 @NonNull private TelephonySubscriptionSnapshot mLastSnapshot; 99 @Nullable private PersistableBundleWrapper mCarrierConfig; 100 private boolean mIsQuitting = false; 101 102 @Nullable private UnderlyingNetworkRecord mCurrentRecord; 103 @Nullable private UnderlyingNetworkRecord.Builder mRecordInProgress; 104 UnderlyingNetworkController( @onNull VcnContext vcnContext, @NonNull VcnGatewayConnectionConfig connectionConfig, @NonNull ParcelUuid subscriptionGroup, @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull UnderlyingNetworkControllerCallback cb)105 public UnderlyingNetworkController( 106 @NonNull VcnContext vcnContext, 107 @NonNull VcnGatewayConnectionConfig connectionConfig, 108 @NonNull ParcelUuid subscriptionGroup, 109 @NonNull TelephonySubscriptionSnapshot snapshot, 110 @NonNull UnderlyingNetworkControllerCallback cb) { 111 this(vcnContext, connectionConfig, subscriptionGroup, snapshot, cb, new Dependencies()); 112 } 113 114 @VisibleForTesting(visibility = Visibility.PRIVATE) UnderlyingNetworkController( @onNull VcnContext vcnContext, @NonNull VcnGatewayConnectionConfig connectionConfig, @NonNull ParcelUuid subscriptionGroup, @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull UnderlyingNetworkControllerCallback cb, @NonNull Dependencies deps)115 UnderlyingNetworkController( 116 @NonNull VcnContext vcnContext, 117 @NonNull VcnGatewayConnectionConfig connectionConfig, 118 @NonNull ParcelUuid subscriptionGroup, 119 @NonNull TelephonySubscriptionSnapshot snapshot, 120 @NonNull UnderlyingNetworkControllerCallback cb, 121 @NonNull Dependencies deps) { 122 mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext"); 123 mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig"); 124 mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup"); 125 mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot"); 126 mCb = Objects.requireNonNull(cb, "Missing cb"); 127 mDeps = Objects.requireNonNull(deps, "Missing deps"); 128 129 mHandler = new Handler(mVcnContext.getLooper()); 130 131 mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class); 132 mVcnContext 133 .getContext() 134 .getSystemService(TelephonyManager.class) 135 .registerTelephonyCallback(new HandlerExecutor(mHandler), mActiveDataSubIdListener); 136 137 mCarrierConfig = mLastSnapshot.getCarrierConfigForSubGrp(mSubscriptionGroup); 138 139 registerOrUpdateNetworkRequests(); 140 } 141 142 private static class CapabilityMatchCriteria { 143 public final int capability; 144 public final int matchCriteria; 145 CapabilityMatchCriteria(int capability, int matchCriteria)146 CapabilityMatchCriteria(int capability, int matchCriteria) { 147 this.capability = capability; 148 this.matchCriteria = matchCriteria; 149 } 150 151 @Override hashCode()152 public int hashCode() { 153 return Objects.hash(capability, matchCriteria); 154 } 155 156 @Override equals(@ullable Object other)157 public boolean equals(@Nullable Object other) { 158 if (!(other instanceof CapabilityMatchCriteria)) { 159 return false; 160 } 161 162 final CapabilityMatchCriteria rhs = (CapabilityMatchCriteria) other; 163 return capability == rhs.capability && matchCriteria == rhs.matchCriteria; 164 } 165 } 166 dedupAndGetCapRequirementsForCell( VcnGatewayConnectionConfig connectionConfig)167 private static Set<Set<CapabilityMatchCriteria>> dedupAndGetCapRequirementsForCell( 168 VcnGatewayConnectionConfig connectionConfig) { 169 final Set<Set<CapabilityMatchCriteria>> dedupedCapsMatchSets = new ArraySet<>(); 170 171 for (VcnUnderlyingNetworkTemplate template : 172 connectionConfig.getVcnUnderlyingNetworkPriorities()) { 173 if (template instanceof VcnCellUnderlyingNetworkTemplate) { 174 final Set<CapabilityMatchCriteria> capsMatchSet = new ArraySet<>(); 175 176 for (Map.Entry<Integer, Integer> entry : 177 ((VcnCellUnderlyingNetworkTemplate) template) 178 .getCapabilitiesMatchCriteria() 179 .entrySet()) { 180 181 final int capability = entry.getKey(); 182 final int matchCriteria = entry.getValue(); 183 if (matchCriteria != MATCH_ANY) { 184 capsMatchSet.add(new CapabilityMatchCriteria(capability, matchCriteria)); 185 } 186 } 187 188 dedupedCapsMatchSets.add(capsMatchSet); 189 } 190 } 191 192 dedupedCapsMatchSets.add( 193 Collections.singleton( 194 new CapabilityMatchCriteria( 195 NetworkCapabilities.NET_CAPABILITY_INTERNET, MATCH_REQUIRED))); 196 return dedupedCapsMatchSets; 197 } 198 registerOrUpdateNetworkRequests()199 private void registerOrUpdateNetworkRequests() { 200 NetworkCallback oldRouteSelectionCallback = mRouteSelectionCallback; 201 NetworkCallback oldWifiCallback = mWifiBringupCallback; 202 NetworkCallback oldWifiEntryRssiThresholdCallback = mWifiEntryRssiThresholdCallback; 203 NetworkCallback oldWifiExitRssiThresholdCallback = mWifiExitRssiThresholdCallback; 204 List<NetworkCallback> oldCellCallbacks = new ArrayList<>(mCellBringupCallbacks); 205 mCellBringupCallbacks.clear(); 206 207 if (mVcnContext.isFlagNetworkMetricMonitorEnabled() 208 && mVcnContext.isFlagIpSecTransformStateEnabled()) { 209 for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) { 210 evaluator.close(); 211 } 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(new TelephonyNetworkSpecifier(subId)); 364 365 for (CapabilityMatchCriteria capMatchCriteria : capsMatchCriteria) { 366 final int cap = capMatchCriteria.capability; 367 final int matchCriteria = capMatchCriteria.matchCriteria; 368 369 if (matchCriteria == MATCH_REQUIRED) { 370 nrBuilder.addCapability(cap); 371 } else if (matchCriteria == MATCH_FORBIDDEN) { 372 nrBuilder.addForbiddenCapability(cap); 373 } 374 } 375 376 return nrBuilder.build(); 377 } 378 379 /** 380 * Builds and returns a NetworkRequest builder common to all Underlying Network requests 381 */ getBaseNetworkRequestBuilder()382 private NetworkRequest.Builder getBaseNetworkRequestBuilder() { 383 return new NetworkRequest.Builder() 384 .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) 385 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) 386 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); 387 } 388 389 /** Builds and returns a NetworkRequest for the given subIds to match Test Networks. */ getTestNetworkRequest(@onNull Set<Integer> subIds)390 private NetworkRequest getTestNetworkRequest(@NonNull Set<Integer> subIds) { 391 return new NetworkRequest.Builder() 392 .clearCapabilities() 393 .addTransportType(NetworkCapabilities.TRANSPORT_TEST) 394 .setSubscriptionIds(subIds) 395 .build(); 396 } 397 398 /** 399 * Update this UnderlyingNetworkController's TelephonySubscriptionSnapshot. 400 * 401 * <p>Updating the TelephonySubscriptionSnapshot will cause this UnderlyingNetworkController to 402 * reevaluate its NetworkBringupCallbacks. This may result in NetworkRequests being registered 403 * or unregistered if the subIds mapped to the this Tracker's SubscriptionGroup change. 404 */ updateSubscriptionSnapshot(@onNull TelephonySubscriptionSnapshot newSnapshot)405 public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot newSnapshot) { 406 Objects.requireNonNull(newSnapshot, "Missing newSnapshot"); 407 408 final TelephonySubscriptionSnapshot oldSnapshot = mLastSnapshot; 409 mLastSnapshot = newSnapshot; 410 411 // Update carrier config 412 mCarrierConfig = mLastSnapshot.getCarrierConfigForSubGrp(mSubscriptionGroup); 413 414 // Make sure all evaluators use the same updated TelephonySubscriptionSnapshot and carrier 415 // config to calculate their cached priority classes. For simplicity, the 416 // UnderlyingNetworkController does not listen for changes in VCN-related carrier config 417 // keys, and changes are applied at restart of the VcnGatewayConnection 418 for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) { 419 evaluator.reevaluate( 420 mConnectionConfig.getVcnUnderlyingNetworkPriorities(), 421 mSubscriptionGroup, 422 mLastSnapshot, 423 mCarrierConfig); 424 } 425 426 // Only trigger re-registration if subIds in this group have changed 427 if (oldSnapshot 428 .getAllSubIdsInGroup(mSubscriptionGroup) 429 .equals(newSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))) { 430 431 if (mVcnContext.isFlagNetworkMetricMonitorEnabled() 432 && mVcnContext.isFlagIpSecTransformStateEnabled()) { 433 reevaluateNetworks(); 434 } 435 return; 436 } 437 registerOrUpdateNetworkRequests(); 438 } 439 440 /** 441 * Pass the IpSecTransform of the VCN to UnderlyingNetworkController for metric monitoring 442 * 443 * <p>Caller MUST call it when IpSecTransforms have been created for VCN creation or migration 444 */ updateInboundTransform( @onNull UnderlyingNetworkRecord currentNetwork, @NonNull IpSecTransform transform)445 public void updateInboundTransform( 446 @NonNull UnderlyingNetworkRecord currentNetwork, @NonNull IpSecTransform transform) { 447 if (!mVcnContext.isFlagNetworkMetricMonitorEnabled() 448 || !mVcnContext.isFlagIpSecTransformStateEnabled()) { 449 logWtf("#updateInboundTransform: unexpected call; flags missing"); 450 return; 451 } 452 453 Objects.requireNonNull(currentNetwork, "currentNetwork is null"); 454 Objects.requireNonNull(transform, "transform is null"); 455 456 if (mCurrentRecord == null 457 || mRouteSelectionCallback == null 458 || !Objects.equals(currentNetwork.network, mCurrentRecord.network)) { 459 // The caller (VcnGatewayConnection) is out-of-dated. Ignore this call. 460 return; 461 } 462 463 mUnderlyingNetworkRecords.get(mCurrentRecord.network).setInboundTransform(transform); 464 } 465 466 /** Tears down this Tracker, and releases all underlying network requests. */ teardown()467 public void teardown() { 468 mVcnContext.ensureRunningOnLooperThread(); 469 mIsQuitting = true; 470 471 // Will unregister all existing callbacks, but not register new ones due to quitting flag. 472 registerOrUpdateNetworkRequests(); 473 474 mVcnContext 475 .getContext() 476 .getSystemService(TelephonyManager.class) 477 .unregisterTelephonyCallback(mActiveDataSubIdListener); 478 } 479 getSortedUnderlyingNetworks()480 private TreeSet<UnderlyingNetworkEvaluator> getSortedUnderlyingNetworks() { 481 TreeSet<UnderlyingNetworkEvaluator> sorted = 482 new TreeSet<>(UnderlyingNetworkEvaluator.getComparator(mVcnContext)); 483 484 for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) { 485 if (evaluator.getPriorityClass() != NetworkPriorityClassifier.PRIORITY_INVALID) { 486 sorted.add(evaluator); 487 } 488 } 489 490 return sorted; 491 } 492 reevaluateNetworks()493 private void reevaluateNetworks() { 494 if (mIsQuitting || mRouteSelectionCallback == null) { 495 return; // UnderlyingNetworkController has quit. 496 } 497 498 TreeSet<UnderlyingNetworkEvaluator> sorted = getSortedUnderlyingNetworks(); 499 500 UnderlyingNetworkEvaluator candidateEvaluator = sorted.isEmpty() ? null : sorted.first(); 501 UnderlyingNetworkRecord candidate = 502 candidateEvaluator == null ? null : candidateEvaluator.getNetworkRecord(); 503 if (Objects.equals(mCurrentRecord, candidate)) { 504 return; 505 } 506 507 String allNetworkPriorities = ""; 508 for (UnderlyingNetworkEvaluator recordEvaluator : sorted) { 509 if (!allNetworkPriorities.isEmpty()) { 510 allNetworkPriorities += ", "; 511 } 512 allNetworkPriorities += 513 recordEvaluator.getNetwork() + ": " + recordEvaluator.getPriorityClass(); 514 } 515 516 if (!UnderlyingNetworkRecord.isSameNetwork(mCurrentRecord, candidate)) { 517 logInfo( 518 "Selected network changed to " 519 + (candidate == null ? null : candidate.network) 520 + ", selected from list: " 521 + allNetworkPriorities); 522 } 523 524 mCurrentRecord = candidate; 525 mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord); 526 527 // Need to update all evaluators to ensure the previously selected one is unselected 528 for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) { 529 evaluator.setIsSelected( 530 candidateEvaluator == evaluator, 531 mConnectionConfig.getVcnUnderlyingNetworkPriorities(), 532 mSubscriptionGroup, 533 mLastSnapshot, 534 mCarrierConfig); 535 } 536 } 537 538 /** 539 * NetworkBringupCallback is used to keep background, VCN-managed Networks from being reaped. 540 * 541 * <p>NetworkBringupCallback only exists to prevent matching (VCN-managed) Networks from being 542 * reaped, and no action is taken on any events firing. 543 */ 544 @VisibleForTesting 545 class NetworkBringupCallback extends NetworkCallback {} 546 547 /** 548 * RouteSelectionCallback is used to select the "best" underlying Network. 549 * 550 * <p>The "best" network is determined by ConnectivityService, which is treated as a source of 551 * truth. 552 */ 553 @VisibleForTesting 554 class UnderlyingNetworkListener extends NetworkCallback { UnderlyingNetworkListener()555 UnderlyingNetworkListener() { 556 super(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO); 557 } 558 559 @Override onAvailable(@onNull Network network)560 public void onAvailable(@NonNull Network network) { 561 mUnderlyingNetworkRecords.put( 562 network, 563 mDeps.newUnderlyingNetworkEvaluator( 564 mVcnContext, 565 network, 566 mConnectionConfig.getVcnUnderlyingNetworkPriorities(), 567 mSubscriptionGroup, 568 mLastSnapshot, 569 mCarrierConfig, 570 new NetworkEvaluatorCallbackImpl())); 571 } 572 573 @Override onLost(@onNull Network network)574 public void onLost(@NonNull Network network) { 575 if (mVcnContext.isFlagNetworkMetricMonitorEnabled() 576 && mVcnContext.isFlagIpSecTransformStateEnabled()) { 577 mUnderlyingNetworkRecords.get(network).close(); 578 } 579 580 mUnderlyingNetworkRecords.remove(network); 581 582 reevaluateNetworks(); 583 } 584 585 @Override onCapabilitiesChanged( @onNull Network network, @NonNull NetworkCapabilities networkCapabilities)586 public void onCapabilitiesChanged( 587 @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) { 588 final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network); 589 if (evaluator == null) { 590 logWtf("Got capabilities change for unknown key: " + network); 591 return; 592 } 593 594 evaluator.setNetworkCapabilities( 595 networkCapabilities, 596 mConnectionConfig.getVcnUnderlyingNetworkPriorities(), 597 mSubscriptionGroup, 598 mLastSnapshot, 599 mCarrierConfig); 600 601 if (evaluator.isValid()) { 602 reevaluateNetworks(); 603 } 604 } 605 606 @Override onLinkPropertiesChanged( @onNull Network network, @NonNull LinkProperties linkProperties)607 public void onLinkPropertiesChanged( 608 @NonNull Network network, @NonNull LinkProperties linkProperties) { 609 final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network); 610 if (evaluator == null) { 611 logWtf("Got link properties change for unknown key: " + network); 612 return; 613 } 614 615 evaluator.setLinkProperties( 616 linkProperties, 617 mConnectionConfig.getVcnUnderlyingNetworkPriorities(), 618 mSubscriptionGroup, 619 mLastSnapshot, 620 mCarrierConfig); 621 622 if (evaluator.isValid()) { 623 reevaluateNetworks(); 624 } 625 } 626 627 @Override onBlockedStatusChanged(@onNull Network network, boolean isBlocked)628 public void onBlockedStatusChanged(@NonNull Network network, boolean isBlocked) { 629 final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network); 630 if (evaluator == null) { 631 logWtf("Got blocked status change for unknown key: " + network); 632 return; 633 } 634 635 evaluator.setIsBlocked( 636 isBlocked, 637 mConnectionConfig.getVcnUnderlyingNetworkPriorities(), 638 mSubscriptionGroup, 639 mLastSnapshot, 640 mCarrierConfig); 641 642 if (evaluator.isValid()) { 643 reevaluateNetworks(); 644 } 645 } 646 } 647 648 @VisibleForTesting 649 class NetworkEvaluatorCallbackImpl implements NetworkEvaluatorCallback { 650 @Override onEvaluationResultChanged()651 public void onEvaluationResultChanged() { 652 if (!mVcnContext.isFlagNetworkMetricMonitorEnabled() 653 || !mVcnContext.isFlagIpSecTransformStateEnabled()) { 654 logWtf("#onEvaluationResultChanged: unexpected call; flags missing"); 655 return; 656 } 657 658 mVcnContext.ensureRunningOnLooperThread(); 659 reevaluateNetworks(); 660 } 661 } 662 getLogPrefix()663 private String getLogPrefix() { 664 return "(" 665 + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup) 666 + "-" 667 + mConnectionConfig.getGatewayConnectionName() 668 + "-" 669 + System.identityHashCode(this) 670 + ") "; 671 } 672 getTagLogPrefix()673 private String getTagLogPrefix() { 674 return "[ " + TAG + " " + getLogPrefix() + "]"; 675 } 676 logInfo(String msg)677 private void logInfo(String msg) { 678 Slog.i(TAG, getLogPrefix() + msg); 679 LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg); 680 } 681 logInfo(String msg, Throwable tr)682 private void logInfo(String msg, Throwable tr) { 683 Slog.i(TAG, getLogPrefix() + msg, tr); 684 LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg + tr); 685 } 686 logWtf(String msg)687 private void logWtf(String msg) { 688 Slog.wtf(TAG, msg); 689 LOCAL_LOG.log(TAG + "[WTF ] " + getTagLogPrefix() + msg); 690 } 691 logWtf(String msg, Throwable tr)692 private void logWtf(String msg, Throwable tr) { 693 Slog.wtf(TAG, msg, tr); 694 LOCAL_LOG.log(TAG + "[WTF ] " + getTagLogPrefix() + msg + tr); 695 } 696 697 /** Dumps the state of this record for logging and debugging purposes. */ dump(IndentingPrintWriter pw)698 public void dump(IndentingPrintWriter pw) { 699 pw.println("UnderlyingNetworkController:"); 700 pw.increaseIndent(); 701 702 pw.println("Carrier WiFi Entry Threshold: " + getWifiEntryRssiThreshold(mCarrierConfig)); 703 pw.println("Carrier WiFi Exit Threshold: " + getWifiExitRssiThreshold(mCarrierConfig)); 704 pw.println( 705 "Currently selected: " + (mCurrentRecord == null ? null : mCurrentRecord.network)); 706 707 pw.println("VcnUnderlyingNetworkTemplate list:"); 708 pw.increaseIndent(); 709 int index = 0; 710 for (VcnUnderlyingNetworkTemplate priority : 711 mConnectionConfig.getVcnUnderlyingNetworkPriorities()) { 712 pw.println("Priority index: " + index); 713 priority.dump(pw); 714 index++; 715 } 716 pw.decreaseIndent(); 717 pw.println(); 718 719 pw.println("Underlying networks:"); 720 pw.increaseIndent(); 721 if (mRouteSelectionCallback != null) { 722 for (UnderlyingNetworkEvaluator recordEvaluator : getSortedUnderlyingNetworks()) { 723 recordEvaluator.dump(pw); 724 } 725 } 726 pw.decreaseIndent(); 727 pw.println(); 728 729 pw.decreaseIndent(); 730 } 731 732 private class VcnActiveDataSubscriptionIdListener extends TelephonyCallback 733 implements ActiveDataSubscriptionIdListener { 734 @Override onActiveDataSubscriptionIdChanged(int subId)735 public void onActiveDataSubscriptionIdChanged(int subId) { 736 reevaluateNetworks(); 737 } 738 } 739 740 /** Callbacks for being notified of the changes in, or to the selected underlying network. */ 741 public interface UnderlyingNetworkControllerCallback { 742 /** 743 * Fired when a new underlying network is selected, or properties have changed. 744 * 745 * <p>This callback does NOT signal a mobility event. 746 * 747 * @param underlyingNetworkRecord The details of the new underlying network 748 */ onSelectedUnderlyingNetworkChanged( @ullable UnderlyingNetworkRecord underlyingNetworkRecord)749 void onSelectedUnderlyingNetworkChanged( 750 @Nullable UnderlyingNetworkRecord underlyingNetworkRecord); 751 } 752 753 @VisibleForTesting(visibility = Visibility.PRIVATE) 754 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)755 public UnderlyingNetworkEvaluator newUnderlyingNetworkEvaluator( 756 @NonNull VcnContext vcnContext, 757 @NonNull Network network, 758 @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, 759 @NonNull ParcelUuid subscriptionGroup, 760 @NonNull TelephonySubscriptionSnapshot lastSnapshot, 761 @Nullable PersistableBundleWrapper carrierConfig, 762 @NonNull NetworkEvaluatorCallback evaluatorCallback) { 763 return new UnderlyingNetworkEvaluator( 764 vcnContext, 765 network, 766 underlyingNetworkTemplates, 767 subscriptionGroup, 768 lastSnapshot, 769 carrierConfig, 770 evaluatorCallback); 771 } 772 } 773 } 774