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