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; 18 19 import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; 20 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; 21 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; 22 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; 23 import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE; 24 import static android.net.vcn.VcnManager.VCN_STATUS_CODE_INACTIVE; 25 import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE; 26 27 import static com.android.server.VcnManagementService.LOCAL_LOG; 28 import static com.android.server.VcnManagementService.VDBG; 29 30 import android.annotation.NonNull; 31 import android.annotation.Nullable; 32 import android.content.ContentResolver; 33 import android.database.ContentObserver; 34 import android.net.NetworkCapabilities; 35 import android.net.NetworkRequest; 36 import android.net.NetworkScore; 37 import android.net.Uri; 38 import android.net.vcn.VcnConfig; 39 import android.net.vcn.VcnGatewayConnectionConfig; 40 import android.net.vcn.VcnManager.VcnErrorCode; 41 import android.os.Handler; 42 import android.os.HandlerExecutor; 43 import android.os.Message; 44 import android.os.ParcelUuid; 45 import android.provider.Settings; 46 import android.telephony.TelephonyCallback; 47 import android.telephony.TelephonyManager; 48 import android.util.ArrayMap; 49 import android.util.ArraySet; 50 import android.util.Slog; 51 52 import com.android.internal.annotations.VisibleForTesting; 53 import com.android.internal.annotations.VisibleForTesting.Visibility; 54 import com.android.internal.util.IndentingPrintWriter; 55 import com.android.server.VcnManagementService.VcnCallback; 56 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; 57 import com.android.server.vcn.util.LogUtils; 58 59 import java.util.Arrays; 60 import java.util.Collections; 61 import java.util.HashMap; 62 import java.util.HashSet; 63 import java.util.Iterator; 64 import java.util.List; 65 import java.util.Map; 66 import java.util.Map.Entry; 67 import java.util.Objects; 68 import java.util.Set; 69 70 /** 71 * Represents an single instance of a VCN. 72 * 73 * <p>Each Vcn instance manages all {@link VcnGatewayConnection}(s) for a given subscription group, 74 * including per-capability networks, network selection, and multi-homing. 75 * 76 * @hide 77 */ 78 public class Vcn extends Handler { 79 private static final String TAG = Vcn.class.getSimpleName(); 80 81 private static final int VCN_LEGACY_SCORE_INT = 52; 82 83 private static final List<Integer> CAPS_REQUIRING_MOBILE_DATA = 84 Arrays.asList(NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN); 85 86 private static final int MSG_EVENT_BASE = 0; 87 private static final int MSG_CMD_BASE = 100; 88 89 /** 90 * A carrier app updated the configuration. 91 * 92 * <p>Triggers update of config, re-evaluating all active and underlying networks. 93 * 94 * @param obj VcnConfig 95 */ 96 private static final int MSG_EVENT_CONFIG_UPDATED = MSG_EVENT_BASE; 97 98 /** 99 * A NetworkRequest was added or updated. 100 * 101 * <p>Triggers an evaluation of all active networks, bringing up a new one if necessary. 102 * 103 * @param obj NetworkRequest 104 */ 105 private static final int MSG_EVENT_NETWORK_REQUESTED = MSG_EVENT_BASE + 1; 106 107 /** 108 * The TelephonySubscriptionSnapshot tracked by VcnManagementService has changed. 109 * 110 * <p>This updated snapshot should be cached locally and passed to all VcnGatewayConnections. 111 * 112 * @param obj TelephonySubscriptionSnapshot 113 */ 114 private static final int MSG_EVENT_SUBSCRIPTIONS_CHANGED = MSG_EVENT_BASE + 2; 115 116 /** 117 * A GatewayConnection owned by this VCN quit. 118 * 119 * @param obj VcnGatewayConnectionConfig 120 */ 121 private static final int MSG_EVENT_GATEWAY_CONNECTION_QUIT = MSG_EVENT_BASE + 3; 122 123 /** 124 * Triggers reevaluation of safe mode conditions. 125 * 126 * <p>Upon entering safe mode, the VCN will only provide gateway connections opportunistically, 127 * leaving the underlying networks marked as NOT_VCN_MANAGED. 128 * 129 * <p>Any VcnGatewayConnection in safe mode will result in the entire Vcn instance being put 130 * into safe mode. Upon receiving this message, the Vcn MUST query all VcnGatewayConnections to 131 * determine if any are in safe mode. 132 */ 133 private static final int MSG_EVENT_SAFE_MODE_STATE_CHANGED = MSG_EVENT_BASE + 4; 134 135 /** 136 * Triggers reevaluation of mobile data enabled conditions. 137 * 138 * <p>Upon this notification, the VCN will check if any of the underlying subIds have mobile 139 * data enabled. If not, the VCN will restart any GatewayConnections providing INTERNET or DUN 140 * with the current mobile data toggle status. 141 */ 142 private static final int MSG_EVENT_MOBILE_DATA_TOGGLED = MSG_EVENT_BASE + 5; 143 144 /** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */ 145 private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE; 146 147 @NonNull private final VcnContext mVcnContext; 148 @NonNull private final ParcelUuid mSubscriptionGroup; 149 @NonNull private final Dependencies mDeps; 150 @NonNull private final VcnNetworkRequestListener mRequestListener; 151 @NonNull private final VcnCallback mVcnCallback; 152 @NonNull private final VcnContentResolver mContentResolver; 153 @NonNull private final ContentObserver mMobileDataSettingsObserver; 154 155 @NonNull 156 private final Map<Integer, VcnUserMobileDataStateListener> mMobileDataStateListeners = 157 new ArrayMap<>(); 158 159 /** 160 * Map containing all VcnGatewayConnections and their VcnGatewayConnectionConfigs. 161 * 162 * <p>Due to potential for race conditions, VcnGatewayConnections MUST only be created and added 163 * to this map in {@link #handleNetworkRequested(NetworkRequest, int, int)}, when a VCN receives 164 * a NetworkRequest that matches a VcnGatewayConnectionConfig for this VCN's VcnConfig. 165 * 166 * <p>A VcnGatewayConnection instance MUST NEVER overwrite an existing instance - otherwise 167 * there is potential for a orphaned VcnGatewayConnection instance that does not get properly 168 * shut down. 169 * 170 * <p>Due to potential for race conditions, VcnGatewayConnections MUST only be removed from this 171 * map once they have finished tearing down, which is reported to this VCN via {@link 172 * VcnGatewayStatusCallback#onQuit()}. Once this is done, all NetworkRequests are retrieved from 173 * the NetworkProvider so that another VcnGatewayConnectionConfig can match the 174 * previously-matched request. 175 */ 176 // TODO(b/182533200): remove the invariant on VcnGatewayConnection lifecycles 177 @NonNull 178 private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections = 179 new HashMap<>(); 180 181 @NonNull private VcnConfig mConfig; 182 @NonNull private TelephonySubscriptionSnapshot mLastSnapshot; 183 184 /** 185 * The current status of this Vcn instance 186 * 187 * <p>The value will be {@link VCN_STATUS_CODE_ACTIVE} while all VcnGatewayConnections are in 188 * good standing, {@link VCN_STATUS_CODE_SAFE_MODE} if any VcnGatewayConnections are in safe 189 * mode, and {@link VCN_STATUS_CODE_INACTIVE} once a teardown has been commanded. 190 */ 191 // Accessed from different threads, but always under lock in VcnManagementService 192 private volatile int mCurrentStatus = VCN_STATUS_CODE_ACTIVE; 193 194 private boolean mIsMobileDataEnabled = false; 195 Vcn( @onNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config, @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull VcnCallback vcnCallback)196 public Vcn( 197 @NonNull VcnContext vcnContext, 198 @NonNull ParcelUuid subscriptionGroup, 199 @NonNull VcnConfig config, 200 @NonNull TelephonySubscriptionSnapshot snapshot, 201 @NonNull VcnCallback vcnCallback) { 202 this(vcnContext, subscriptionGroup, config, snapshot, vcnCallback, new Dependencies()); 203 } 204 205 @VisibleForTesting(visibility = Visibility.PRIVATE) Vcn( @onNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config, @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull VcnCallback vcnCallback, @NonNull Dependencies deps)206 public Vcn( 207 @NonNull VcnContext vcnContext, 208 @NonNull ParcelUuid subscriptionGroup, 209 @NonNull VcnConfig config, 210 @NonNull TelephonySubscriptionSnapshot snapshot, 211 @NonNull VcnCallback vcnCallback, 212 @NonNull Dependencies deps) { 213 super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper()); 214 mVcnContext = vcnContext; 215 mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup"); 216 mVcnCallback = Objects.requireNonNull(vcnCallback, "Missing vcnCallback"); 217 mDeps = Objects.requireNonNull(deps, "Missing deps"); 218 mRequestListener = new VcnNetworkRequestListener(); 219 mContentResolver = mDeps.newVcnContentResolver(mVcnContext); 220 mMobileDataSettingsObserver = new VcnMobileDataContentObserver(this /* handler */); 221 222 final Uri uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA); 223 mContentResolver.registerContentObserver( 224 uri, true /* notifyForDescendants */, mMobileDataSettingsObserver); 225 226 mConfig = Objects.requireNonNull(config, "Missing config"); 227 mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot"); 228 229 // Update mIsMobileDataEnabled before starting handling of NetworkRequests. 230 mIsMobileDataEnabled = getMobileDataStatus(); 231 232 // Register mobile data state listeners. 233 updateMobileDataStateListeners(); 234 235 // Register to receive cached and future NetworkRequests 236 mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener); 237 } 238 239 /** Asynchronously updates the configuration and triggers a re-evaluation of Networks */ updateConfig(@onNull VcnConfig config)240 public void updateConfig(@NonNull VcnConfig config) { 241 Objects.requireNonNull(config, "Missing config"); 242 243 sendMessage(obtainMessage(MSG_EVENT_CONFIG_UPDATED, config)); 244 } 245 246 /** Asynchronously updates the Subscription snapshot for this VCN. */ updateSubscriptionSnapshot(@onNull TelephonySubscriptionSnapshot snapshot)247 public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) { 248 Objects.requireNonNull(snapshot, "Missing snapshot"); 249 250 sendMessage(obtainMessage(MSG_EVENT_SUBSCRIPTIONS_CHANGED, snapshot)); 251 } 252 253 /** Asynchronously tears down this Vcn instance, including VcnGatewayConnection(s) */ teardownAsynchronously()254 public void teardownAsynchronously() { 255 sendMessageAtFrontOfQueue(obtainMessage(MSG_CMD_TEARDOWN)); 256 } 257 258 /** Synchronously retrieves the current status code. */ getStatus()259 public int getStatus() { 260 return mCurrentStatus; 261 } 262 263 /** Sets the status of this VCN */ 264 @VisibleForTesting(visibility = Visibility.PRIVATE) setStatus(int status)265 public void setStatus(int status) { 266 mCurrentStatus = status; 267 } 268 269 /** Get current Gateways for testing purposes */ 270 @VisibleForTesting(visibility = Visibility.PRIVATE) getVcnGatewayConnections()271 public Set<VcnGatewayConnection> getVcnGatewayConnections() { 272 return Collections.unmodifiableSet(new HashSet<>(mVcnGatewayConnections.values())); 273 } 274 275 /** Get current Configs and Gateways for testing purposes */ 276 @VisibleForTesting(visibility = Visibility.PRIVATE) 277 public Map<VcnGatewayConnectionConfig, VcnGatewayConnection> getVcnGatewayConnectionConfigMap()278 getVcnGatewayConnectionConfigMap() { 279 return Collections.unmodifiableMap(new HashMap<>(mVcnGatewayConnections)); 280 } 281 282 private class VcnNetworkRequestListener implements VcnNetworkProvider.NetworkRequestListener { 283 @Override onNetworkRequested(@onNull NetworkRequest request)284 public void onNetworkRequested(@NonNull NetworkRequest request) { 285 Objects.requireNonNull(request, "Missing request"); 286 287 sendMessage(obtainMessage(MSG_EVENT_NETWORK_REQUESTED, request)); 288 } 289 } 290 291 @Override handleMessage(@onNull Message msg)292 public void handleMessage(@NonNull Message msg) { 293 if (mCurrentStatus != VCN_STATUS_CODE_ACTIVE 294 && mCurrentStatus != VCN_STATUS_CODE_SAFE_MODE) { 295 return; 296 } 297 298 switch (msg.what) { 299 case MSG_EVENT_CONFIG_UPDATED: 300 handleConfigUpdated((VcnConfig) msg.obj); 301 break; 302 case MSG_EVENT_NETWORK_REQUESTED: 303 handleNetworkRequested((NetworkRequest) msg.obj); 304 break; 305 case MSG_EVENT_SUBSCRIPTIONS_CHANGED: 306 handleSubscriptionsChanged((TelephonySubscriptionSnapshot) msg.obj); 307 break; 308 case MSG_EVENT_GATEWAY_CONNECTION_QUIT: 309 handleGatewayConnectionQuit((VcnGatewayConnectionConfig) msg.obj); 310 break; 311 case MSG_EVENT_SAFE_MODE_STATE_CHANGED: 312 handleSafeModeStatusChanged(); 313 break; 314 case MSG_EVENT_MOBILE_DATA_TOGGLED: 315 handleMobileDataToggled(); 316 break; 317 case MSG_CMD_TEARDOWN: 318 handleTeardown(); 319 break; 320 default: 321 logWtf("Unknown msg.what: " + msg.what); 322 } 323 } 324 handleConfigUpdated(@onNull VcnConfig config)325 private void handleConfigUpdated(@NonNull VcnConfig config) { 326 // TODO: Add a dump function in VcnConfig that omits PII. Until then, use hashCode() 327 logDbg("Config updated: old = " + mConfig.hashCode() + "; new = " + config.hashCode()); 328 329 mConfig = config; 330 331 // Teardown any GatewayConnections whose configs have been removed and get all current 332 // requests 333 for (final Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry : 334 mVcnGatewayConnections.entrySet()) { 335 final VcnGatewayConnectionConfig gatewayConnectionConfig = entry.getKey(); 336 final VcnGatewayConnection gatewayConnection = entry.getValue(); 337 338 // GatewayConnectionConfigs must match exactly (otherwise authentication or 339 // connection details may have changed). 340 if (!mConfig.getGatewayConnectionConfigs().contains(gatewayConnectionConfig)) { 341 if (gatewayConnection == null) { 342 logWtf("Found gatewayConnectionConfig without GatewayConnection"); 343 } else { 344 logInfo( 345 "Config updated, restarting gateway " 346 + gatewayConnection.getLogPrefix()); 347 gatewayConnection.teardownAsynchronously(); 348 } 349 } 350 } 351 352 // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be 353 // satisfied start a new GatewayConnection) 354 mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener); 355 } 356 handleTeardown()357 private void handleTeardown() { 358 logDbg("Tearing down"); 359 mVcnContext.getVcnNetworkProvider().unregisterListener(mRequestListener); 360 361 for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) { 362 gatewayConnection.teardownAsynchronously(); 363 } 364 365 // Unregister MobileDataStateListeners 366 for (VcnUserMobileDataStateListener listener : mMobileDataStateListeners.values()) { 367 getTelephonyManager().unregisterTelephonyCallback(listener); 368 } 369 mMobileDataStateListeners.clear(); 370 371 mCurrentStatus = VCN_STATUS_CODE_INACTIVE; 372 } 373 handleSafeModeStatusChanged()374 private void handleSafeModeStatusChanged() { 375 logVdbg("VcnGatewayConnection safe mode status changed"); 376 boolean hasSafeModeGatewayConnection = false; 377 378 // If any VcnGatewayConnection is in safe mode, mark the entire VCN as being in safe mode 379 for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) { 380 if (gatewayConnection.isInSafeMode()) { 381 hasSafeModeGatewayConnection = true; 382 break; 383 } 384 } 385 386 final int oldStatus = mCurrentStatus; 387 mCurrentStatus = 388 hasSafeModeGatewayConnection ? VCN_STATUS_CODE_SAFE_MODE : VCN_STATUS_CODE_ACTIVE; 389 if (oldStatus != mCurrentStatus) { 390 mVcnCallback.onSafeModeStatusChanged(hasSafeModeGatewayConnection); 391 logInfo( 392 "Safe mode " 393 + (mCurrentStatus == VCN_STATUS_CODE_SAFE_MODE ? "entered" : "exited")); 394 } 395 } 396 handleNetworkRequested(@onNull NetworkRequest request)397 private void handleNetworkRequested(@NonNull NetworkRequest request) { 398 logVdbg("Received request " + request); 399 400 // If preexisting VcnGatewayConnection(s) satisfy request, return 401 for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) { 402 if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) { 403 logVdbg("Request already satisfied by existing VcnGatewayConnection: " + request); 404 return; 405 } 406 } 407 408 // If any supported (but not running) VcnGatewayConnection(s) can satisfy request, bring it 409 // up 410 for (VcnGatewayConnectionConfig gatewayConnectionConfig : 411 mConfig.getGatewayConnectionConfigs()) { 412 if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) { 413 if (getExposedCapabilitiesForMobileDataState(gatewayConnectionConfig).isEmpty()) { 414 // Skip; this network does not provide any services if mobile data is disabled. 415 continue; 416 } 417 418 // This should never happen, by virtue of checking for the above check for 419 // pre-existing VcnGatewayConnections that satisfy a given request, but if state 420 // that affects the satsifying of requests changes, this is theoretically possible. 421 if (mVcnGatewayConnections.containsKey(gatewayConnectionConfig)) { 422 logWtf( 423 "Attempted to bring up VcnGatewayConnection for config " 424 + "with existing VcnGatewayConnection"); 425 return; 426 } 427 428 logInfo("Bringing up new VcnGatewayConnection for request " + request); 429 final VcnGatewayConnection vcnGatewayConnection = 430 mDeps.newVcnGatewayConnection( 431 mVcnContext, 432 mSubscriptionGroup, 433 mLastSnapshot, 434 gatewayConnectionConfig, 435 new VcnGatewayStatusCallbackImpl(gatewayConnectionConfig), 436 mIsMobileDataEnabled); 437 mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection); 438 439 return; 440 } 441 } 442 443 logVdbg("Request could not be fulfilled by VCN: " + request); 444 } 445 getExposedCapabilitiesForMobileDataState( VcnGatewayConnectionConfig gatewayConnectionConfig)446 private Set<Integer> getExposedCapabilitiesForMobileDataState( 447 VcnGatewayConnectionConfig gatewayConnectionConfig) { 448 if (mIsMobileDataEnabled) { 449 return gatewayConnectionConfig.getAllExposedCapabilities(); 450 } 451 452 final Set<Integer> exposedCapsWithoutMobileData = 453 new ArraySet<>(gatewayConnectionConfig.getAllExposedCapabilities()); 454 exposedCapsWithoutMobileData.removeAll(CAPS_REQUIRING_MOBILE_DATA); 455 456 return exposedCapsWithoutMobileData; 457 } 458 handleGatewayConnectionQuit(VcnGatewayConnectionConfig config)459 private void handleGatewayConnectionQuit(VcnGatewayConnectionConfig config) { 460 logInfo("VcnGatewayConnection quit: " + config); 461 mVcnGatewayConnections.remove(config); 462 463 // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be satisfied 464 // start a new GatewayConnection). VCN is always alive here, courtesy of the liveness check 465 // in handleMessage() 466 mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener); 467 } 468 handleSubscriptionsChanged(@onNull TelephonySubscriptionSnapshot snapshot)469 private void handleSubscriptionsChanged(@NonNull TelephonySubscriptionSnapshot snapshot) { 470 mLastSnapshot = snapshot; 471 472 for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) { 473 gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot); 474 } 475 476 updateMobileDataStateListeners(); 477 478 // Update the mobile data state after updating the subscription snapshot as a change in 479 // subIds for a subGroup may affect the mobile data state. 480 handleMobileDataToggled(); 481 } 482 updateMobileDataStateListeners()483 private void updateMobileDataStateListeners() { 484 final Set<Integer> subIdsInGroup = mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup); 485 final HandlerExecutor executor = new HandlerExecutor(this); 486 487 // Register new callbacks 488 for (int subId : subIdsInGroup) { 489 if (!mMobileDataStateListeners.containsKey(subId)) { 490 final VcnUserMobileDataStateListener listener = 491 new VcnUserMobileDataStateListener(); 492 493 getTelephonyManagerForSubid(subId).registerTelephonyCallback(executor, listener); 494 mMobileDataStateListeners.put(subId, listener); 495 } 496 } 497 498 // Unregister old callbacks 499 Iterator<Entry<Integer, VcnUserMobileDataStateListener>> iterator = 500 mMobileDataStateListeners.entrySet().iterator(); 501 while (iterator.hasNext()) { 502 final Entry<Integer, VcnUserMobileDataStateListener> entry = iterator.next(); 503 if (!subIdsInGroup.contains(entry.getKey())) { 504 getTelephonyManager().unregisterTelephonyCallback(entry.getValue()); 505 iterator.remove(); 506 } 507 } 508 } 509 handleMobileDataToggled()510 private void handleMobileDataToggled() { 511 final boolean oldMobileDataEnabledStatus = mIsMobileDataEnabled; 512 mIsMobileDataEnabled = getMobileDataStatus(); 513 514 if (oldMobileDataEnabledStatus != mIsMobileDataEnabled) { 515 // Teardown any GatewayConnections that advertise INTERNET or DUN. If they provide other 516 // services, the VcnGatewayConnections will be restarted without advertising INTERNET or 517 // DUN. 518 for (Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry : 519 mVcnGatewayConnections.entrySet()) { 520 final VcnGatewayConnectionConfig gatewayConnectionConfig = entry.getKey(); 521 final VcnGatewayConnection gatewayConnection = entry.getValue(); 522 523 final Set<Integer> exposedCaps = 524 gatewayConnectionConfig.getAllExposedCapabilities(); 525 if (exposedCaps.contains(NET_CAPABILITY_INTERNET) 526 || exposedCaps.contains(NET_CAPABILITY_DUN)) { 527 if (gatewayConnection == null) { 528 logWtf("Found gatewayConnectionConfig without" + " GatewayConnection"); 529 } else { 530 // TODO(b/184868850): Optimize by restarting NetworkAgents without teardown. 531 gatewayConnection.teardownAsynchronously(); 532 } 533 } 534 } 535 536 // Trigger re-evaluation of all requests; mobile data state impacts supported caps. 537 mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener); 538 539 logInfo("Mobile data " + (mIsMobileDataEnabled ? "enabled" : "disabled")); 540 } 541 } 542 getMobileDataStatus()543 private boolean getMobileDataStatus() { 544 for (int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) { 545 if (getTelephonyManagerForSubid(subId).isDataEnabled()) { 546 return true; 547 } 548 } 549 550 return false; 551 } 552 isRequestSatisfiedByGatewayConnectionConfig( @onNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config)553 private boolean isRequestSatisfiedByGatewayConnectionConfig( 554 @NonNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config) { 555 final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(); 556 builder.addTransportType(TRANSPORT_CELLULAR); 557 builder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); 558 for (int cap : getExposedCapabilitiesForMobileDataState(config)) { 559 builder.addCapability(cap); 560 } 561 562 return request.canBeSatisfiedBy(builder.build()); 563 } 564 getTelephonyManager()565 private TelephonyManager getTelephonyManager() { 566 return mVcnContext.getContext().getSystemService(TelephonyManager.class); 567 } 568 getTelephonyManagerForSubid(int subid)569 private TelephonyManager getTelephonyManagerForSubid(int subid) { 570 return getTelephonyManager().createForSubscriptionId(subid); 571 } 572 getLogPrefix()573 private String getLogPrefix() { 574 return "(" 575 + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup) 576 + "-" 577 + System.identityHashCode(this) 578 + ") "; 579 } 580 logVdbg(String msg)581 private void logVdbg(String msg) { 582 if (VDBG) { 583 Slog.v(TAG, getLogPrefix() + msg); 584 } 585 } 586 logDbg(String msg)587 private void logDbg(String msg) { 588 Slog.d(TAG, getLogPrefix() + msg); 589 } 590 logDbg(String msg, Throwable tr)591 private void logDbg(String msg, Throwable tr) { 592 Slog.d(TAG, getLogPrefix() + msg, tr); 593 } 594 logInfo(String msg)595 private void logInfo(String msg) { 596 Slog.i(TAG, getLogPrefix() + msg); 597 LOCAL_LOG.log(getLogPrefix() + "INFO: " + msg); 598 } 599 logInfo(String msg, Throwable tr)600 private void logInfo(String msg, Throwable tr) { 601 Slog.i(TAG, getLogPrefix() + msg, tr); 602 LOCAL_LOG.log(getLogPrefix() + "INFO: " + msg + tr); 603 } 604 logErr(String msg)605 private void logErr(String msg) { 606 Slog.e(TAG, getLogPrefix() + msg); 607 LOCAL_LOG.log(getLogPrefix() + "ERR: " + msg); 608 } 609 logErr(String msg, Throwable tr)610 private void logErr(String msg, Throwable tr) { 611 Slog.e(TAG, getLogPrefix() + msg, tr); 612 LOCAL_LOG.log(getLogPrefix() + "ERR: " + msg + tr); 613 } 614 logWtf(String msg)615 private void logWtf(String msg) { 616 Slog.wtf(TAG, getLogPrefix() + msg); 617 LOCAL_LOG.log(getLogPrefix() + "WTF: " + msg); 618 } 619 logWtf(String msg, Throwable tr)620 private void logWtf(String msg, Throwable tr) { 621 Slog.wtf(TAG, getLogPrefix() + msg, tr); 622 LOCAL_LOG.log(getLogPrefix() + "WTF: " + msg + tr); 623 } 624 625 /** 626 * Dumps the state of this Vcn for logging and debugging purposes. 627 * 628 * <p>PII and credentials MUST NEVER be dumped here. 629 */ dump(IndentingPrintWriter pw)630 public void dump(IndentingPrintWriter pw) { 631 pw.println("Vcn (" + mSubscriptionGroup + "):"); 632 pw.increaseIndent(); 633 634 pw.println("mCurrentStatus: " + mCurrentStatus); 635 pw.println("mIsMobileDataEnabled: " + mIsMobileDataEnabled); 636 pw.println(); 637 638 pw.println("mVcnGatewayConnections:"); 639 pw.increaseIndent(); 640 for (VcnGatewayConnection gw : mVcnGatewayConnections.values()) { 641 gw.dump(pw); 642 } 643 pw.decreaseIndent(); 644 pw.println(); 645 646 pw.decreaseIndent(); 647 } 648 649 @VisibleForTesting(visibility = Visibility.PRIVATE) isMobileDataEnabled()650 public boolean isMobileDataEnabled() { 651 return mIsMobileDataEnabled; 652 } 653 654 @VisibleForTesting(visibility = Visibility.PRIVATE) setMobileDataEnabled(boolean isMobileDataEnabled)655 public void setMobileDataEnabled(boolean isMobileDataEnabled) { 656 mIsMobileDataEnabled = isMobileDataEnabled; 657 } 658 659 /** Retrieves the network score for a VCN Network */ 660 // Package visibility for use in VcnGatewayConnection and VcnNetworkProvider getNetworkScore()661 static NetworkScore getNetworkScore() { 662 // TODO(b/193687515): Stop setting TRANSPORT_PRIMARY, define a TRANSPORT_VCN, and set in 663 // NetworkOffer/NetworkAgent. 664 return new NetworkScore.Builder() 665 .setLegacyInt(VCN_LEGACY_SCORE_INT) 666 .setTransportPrimary(true) 667 .build(); 668 } 669 670 /** Callback used for passing status signals from a VcnGatewayConnection to its managing Vcn. */ 671 @VisibleForTesting(visibility = Visibility.PACKAGE) 672 public interface VcnGatewayStatusCallback { 673 /** Called by a VcnGatewayConnection to indicate that it's safe mode status has changed. */ onSafeModeStatusChanged()674 void onSafeModeStatusChanged(); 675 676 /** Callback by a VcnGatewayConnection to indicate that an error occurred. */ onGatewayConnectionError( @onNull String gatewayConnectionName, @VcnErrorCode int errorCode, @Nullable String exceptionClass, @Nullable String exceptionMessage)677 void onGatewayConnectionError( 678 @NonNull String gatewayConnectionName, 679 @VcnErrorCode int errorCode, 680 @Nullable String exceptionClass, 681 @Nullable String exceptionMessage); 682 683 /** Called by a VcnGatewayConnection to indicate that it has fully torn down. */ onQuit()684 void onQuit(); 685 } 686 687 private class VcnGatewayStatusCallbackImpl implements VcnGatewayStatusCallback { 688 public final VcnGatewayConnectionConfig mGatewayConnectionConfig; 689 VcnGatewayStatusCallbackImpl(VcnGatewayConnectionConfig gatewayConnectionConfig)690 VcnGatewayStatusCallbackImpl(VcnGatewayConnectionConfig gatewayConnectionConfig) { 691 mGatewayConnectionConfig = gatewayConnectionConfig; 692 } 693 694 @Override onQuit()695 public void onQuit() { 696 sendMessage(obtainMessage(MSG_EVENT_GATEWAY_CONNECTION_QUIT, mGatewayConnectionConfig)); 697 } 698 699 @Override onSafeModeStatusChanged()700 public void onSafeModeStatusChanged() { 701 sendMessage(obtainMessage(MSG_EVENT_SAFE_MODE_STATE_CHANGED)); 702 } 703 704 @Override onGatewayConnectionError( @onNull String gatewayConnectionName, @VcnErrorCode int errorCode, @Nullable String exceptionClass, @Nullable String exceptionMessage)705 public void onGatewayConnectionError( 706 @NonNull String gatewayConnectionName, 707 @VcnErrorCode int errorCode, 708 @Nullable String exceptionClass, 709 @Nullable String exceptionMessage) { 710 mVcnCallback.onGatewayConnectionError( 711 gatewayConnectionName, errorCode, exceptionClass, exceptionMessage); 712 } 713 } 714 715 private class VcnMobileDataContentObserver extends ContentObserver { VcnMobileDataContentObserver(Handler handler)716 private VcnMobileDataContentObserver(Handler handler) { 717 super(handler); 718 } 719 720 @Override onChange(boolean selfChange)721 public void onChange(boolean selfChange) { 722 sendMessage(obtainMessage(MSG_EVENT_MOBILE_DATA_TOGGLED)); 723 } 724 } 725 726 @VisibleForTesting(visibility = Visibility.PRIVATE) 727 class VcnUserMobileDataStateListener extends TelephonyCallback 728 implements TelephonyCallback.UserMobileDataStateListener { 729 730 @Override onUserMobileDataStateChanged(boolean enabled)731 public void onUserMobileDataStateChanged(boolean enabled) { 732 sendMessage(obtainMessage(MSG_EVENT_MOBILE_DATA_TOGGLED)); 733 } 734 } 735 736 /** External dependencies used by Vcn, for injection in tests */ 737 @VisibleForTesting(visibility = Visibility.PRIVATE) 738 public static class Dependencies { 739 /** Builds a new VcnGatewayConnection */ newVcnGatewayConnection( VcnContext vcnContext, ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, VcnGatewayConnectionConfig connectionConfig, VcnGatewayStatusCallback gatewayStatusCallback, boolean isMobileDataEnabled)740 public VcnGatewayConnection newVcnGatewayConnection( 741 VcnContext vcnContext, 742 ParcelUuid subscriptionGroup, 743 TelephonySubscriptionSnapshot snapshot, 744 VcnGatewayConnectionConfig connectionConfig, 745 VcnGatewayStatusCallback gatewayStatusCallback, 746 boolean isMobileDataEnabled) { 747 return new VcnGatewayConnection( 748 vcnContext, 749 subscriptionGroup, 750 snapshot, 751 connectionConfig, 752 gatewayStatusCallback, 753 isMobileDataEnabled); 754 } 755 756 /** Builds a new VcnContentResolver instance */ newVcnContentResolver(VcnContext vcnContext)757 public VcnContentResolver newVcnContentResolver(VcnContext vcnContext) { 758 return new VcnContentResolver(vcnContext); 759 } 760 } 761 762 /** Proxy Implementation of NetworkAgent, used for testing. */ 763 @VisibleForTesting(visibility = Visibility.PRIVATE) 764 public static class VcnContentResolver { 765 private final ContentResolver mImpl; 766 VcnContentResolver(VcnContext vcnContext)767 public VcnContentResolver(VcnContext vcnContext) { 768 mImpl = vcnContext.getContext().getContentResolver(); 769 } 770 771 /** Registers the content observer */ registerContentObserver( @onNull Uri uri, boolean notifyForDescendants, @NonNull ContentObserver observer)772 public void registerContentObserver( 773 @NonNull Uri uri, boolean notifyForDescendants, @NonNull ContentObserver observer) { 774 mImpl.registerContentObserver(uri, notifyForDescendants, observer); 775 } 776 } 777 } 778