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