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