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