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_CONGESTED; 22 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; 23 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; 24 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; 25 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; 26 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; 27 import static android.net.NetworkCapabilities.TRANSPORT_WIFI; 28 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED; 29 import static android.net.vcn.VcnGatewayConnectionConfig.VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY; 30 import static android.net.vcn.VcnManager.VCN_ERROR_CODE_CONFIG_ERROR; 31 import static android.net.vcn.VcnManager.VCN_ERROR_CODE_INTERNAL_ERROR; 32 import static android.net.vcn.VcnManager.VCN_ERROR_CODE_NETWORK_ERROR; 33 34 import static com.android.server.VcnManagementService.LOCAL_LOG; 35 import static com.android.server.VcnManagementService.VDBG; 36 import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; 37 38 import android.annotation.NonNull; 39 import android.annotation.Nullable; 40 import android.content.Context; 41 import android.net.ConnectivityDiagnosticsManager; 42 import android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback; 43 import android.net.ConnectivityManager; 44 import android.net.InetAddresses; 45 import android.net.IpPrefix; 46 import android.net.IpSecManager; 47 import android.net.IpSecManager.IpSecTunnelInterface; 48 import android.net.IpSecManager.ResourceUnavailableException; 49 import android.net.IpSecTransform; 50 import android.net.LinkAddress; 51 import android.net.LinkProperties; 52 import android.net.Network; 53 import android.net.NetworkAgent; 54 import android.net.NetworkAgentConfig; 55 import android.net.NetworkCapabilities; 56 import android.net.NetworkProvider; 57 import android.net.NetworkRequest; 58 import android.net.NetworkScore; 59 import android.net.RouteInfo; 60 import android.net.TelephonyNetworkSpecifier; 61 import android.net.Uri; 62 import android.net.annotations.PolicyDirection; 63 import android.net.ipsec.ike.ChildSaProposal; 64 import android.net.ipsec.ike.ChildSessionCallback; 65 import android.net.ipsec.ike.ChildSessionConfiguration; 66 import android.net.ipsec.ike.ChildSessionParams; 67 import android.net.ipsec.ike.IkeSession; 68 import android.net.ipsec.ike.IkeSessionCallback; 69 import android.net.ipsec.ike.IkeSessionConfiguration; 70 import android.net.ipsec.ike.IkeSessionConnectionInfo; 71 import android.net.ipsec.ike.IkeSessionParams; 72 import android.net.ipsec.ike.IkeTrafficSelector; 73 import android.net.ipsec.ike.IkeTunnelConnectionParams; 74 import android.net.ipsec.ike.TunnelModeChildSessionParams; 75 import android.net.ipsec.ike.exceptions.IkeException; 76 import android.net.ipsec.ike.exceptions.IkeInternalException; 77 import android.net.ipsec.ike.exceptions.IkeProtocolException; 78 import android.net.vcn.VcnGatewayConnectionConfig; 79 import android.net.vcn.VcnManager; 80 import android.net.vcn.VcnTransportInfo; 81 import android.net.wifi.WifiInfo; 82 import android.os.Handler; 83 import android.os.HandlerExecutor; 84 import android.os.Message; 85 import android.os.ParcelUuid; 86 import android.os.PowerManager; 87 import android.os.PowerManager.WakeLock; 88 import android.os.Process; 89 import android.os.SystemClock; 90 import android.provider.Settings; 91 import android.telephony.TelephonyManager; 92 import android.util.ArraySet; 93 import android.util.Slog; 94 95 import com.android.internal.annotations.VisibleForTesting; 96 import com.android.internal.annotations.VisibleForTesting.Visibility; 97 import com.android.internal.util.IndentingPrintWriter; 98 import com.android.internal.util.State; 99 import com.android.internal.util.StateMachine; 100 import com.android.internal.util.WakeupMessage; 101 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; 102 import com.android.server.vcn.Vcn.VcnGatewayStatusCallback; 103 import com.android.server.vcn.routeselection.UnderlyingNetworkController; 104 import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkControllerCallback; 105 import com.android.server.vcn.routeselection.UnderlyingNetworkRecord; 106 import com.android.server.vcn.util.LogUtils; 107 import com.android.server.vcn.util.MtuUtils; 108 import com.android.server.vcn.util.OneWayBoolean; 109 110 import java.io.IOException; 111 import java.net.Inet4Address; 112 import java.net.Inet6Address; 113 import java.net.InetAddress; 114 import java.net.NetworkInterface; 115 import java.util.Arrays; 116 import java.util.Collections; 117 import java.util.List; 118 import java.util.Objects; 119 import java.util.Set; 120 import java.util.concurrent.TimeUnit; 121 import java.util.function.Consumer; 122 123 /** 124 * A single VCN Gateway Connection, providing a single public-facing VCN network. 125 * 126 * <p>This class handles mobility events, performs retries, and tracks safe-mode conditions. 127 * 128 * <pre>Internal state transitions are as follows: 129 * 130 * +----------------------------+ +------------------------------+ 131 * | DisconnectedState | Teardown or | DisconnectingState | 132 * | |<--no available--| | 133 * | Initial state. | underlying | Transitive state for tearing | 134 * +----------------------------+ networks | tearing down an IKE session. | 135 * | +------------------------------+ 136 * | ^ | 137 * Underlying Network Teardown requested | Not tearing down 138 * changed +--or retriable error--+ and has available 139 * | | occurred underlying network 140 * | ^ | 141 * v | v 142 * +----------------------------+ | +------------------------------+ 143 * | ConnectingState |<----------------| RetryTimeoutState | 144 * | | | | | 145 * | Transitive state for | | | Transitive state for | 146 * | starting IKE negotiation. |---+ | handling retriable errors. | 147 * +----------------------------+ | +------------------------------+ 148 * | | 149 * IKE session | 150 * negotiated | 151 * | | 152 * v | 153 * +----------------------------+ ^ 154 * | ConnectedState | | 155 * | | | 156 * | Stable state where | | 157 * | gateway connection is set | | 158 * | up, and Android Network is | | 159 * | connected. |---+ 160 * +----------------------------+ 161 * </pre> 162 * 163 * <p>All messages in VcnGatewayConnection <b>should</b> be enqueued using {@link 164 * #sendMessageAndAcquireWakeLock}. Careful consideration should be given to any uses of {@link 165 * #sendMessage} directly, as they are not guaranteed to be processed in a timely manner (due to the 166 * lack of WakeLocks). 167 * 168 * <p>Any attempt to remove messages from the Handler should be done using {@link 169 * #removeEqualMessages}. This is necessary to ensure that the WakeLock is correctly released when 170 * no messages remain in the Handler queue. 171 * 172 * @hide 173 */ 174 public class VcnGatewayConnection extends StateMachine { 175 private static final String TAG = VcnGatewayConnection.class.getSimpleName(); 176 177 /** Default number of parallel SAs requested */ 178 static final int TUNNEL_AGGREGATION_SA_COUNT_MAX_DEFAULT = 1; 179 180 // Matches DataConnection.NETWORK_TYPE private constant, and magic string from 181 // ConnectivityManager#getNetworkTypeName() 182 @VisibleForTesting(visibility = Visibility.PRIVATE) 183 static final String NETWORK_INFO_NETWORK_TYPE_STRING = "MOBILE"; 184 185 @VisibleForTesting(visibility = Visibility.PRIVATE) 186 static final String NETWORK_INFO_EXTRA_INFO = "VCN"; 187 188 @VisibleForTesting(visibility = Visibility.PRIVATE) 189 static final InetAddress DUMMY_ADDR = InetAddresses.parseNumericAddress("192.0.2.0"); 190 191 @VisibleForTesting(visibility = Visibility.PRIVATE) 192 static final String TEARDOWN_TIMEOUT_ALARM = TAG + "_TEARDOWN_TIMEOUT_ALARM"; 193 194 @VisibleForTesting(visibility = Visibility.PRIVATE) 195 static final String DISCONNECT_REQUEST_ALARM = TAG + "_DISCONNECT_REQUEST_ALARM"; 196 197 @VisibleForTesting(visibility = Visibility.PRIVATE) 198 static final String RETRY_TIMEOUT_ALARM = TAG + "_RETRY_TIMEOUT_ALARM"; 199 200 @VisibleForTesting(visibility = Visibility.PRIVATE) 201 static final String SAFEMODE_TIMEOUT_ALARM = TAG + "_SAFEMODE_TIMEOUT_ALARM"; 202 203 private static final int[] MERGED_CAPABILITIES = 204 new int[] {NET_CAPABILITY_NOT_METERED, NET_CAPABILITY_NOT_ROAMING}; 205 private static final int ARG_NOT_PRESENT = Integer.MIN_VALUE; 206 207 private static final String DISCONNECT_REASON_INTERNAL_ERROR = "Uncaught exception: "; 208 private static final String DISCONNECT_REASON_UNDERLYING_NETWORK_LOST = 209 "Underlying Network lost"; 210 private static final String DISCONNECT_REASON_NETWORK_AGENT_UNWANTED = 211 "NetworkAgent was unwanted"; 212 private static final String DISCONNECT_REASON_TEARDOWN = "teardown() called on VcnTunnel"; 213 private static final int TOKEN_ALL = Integer.MIN_VALUE; 214 215 @VisibleForTesting(visibility = Visibility.PRIVATE) 216 static final int NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS = 30; 217 218 @VisibleForTesting(visibility = Visibility.PRIVATE) 219 static final int TEARDOWN_TIMEOUT_SECONDS = 5; 220 221 @VisibleForTesting(visibility = Visibility.PRIVATE) 222 static final int SAFEMODE_TIMEOUT_SECONDS = 30; 223 private static final int SAFEMODE_TIMEOUT_SECONDS_TEST_MODE = 10; 224 225 private interface EventInfo {} 226 227 /** 228 * Sent when there are changes to the underlying network (per the UnderlyingNetworkController). 229 * 230 * <p>May indicate an entirely new underlying network, OR a change in network properties. 231 * 232 * <p>Relevant in ALL states. 233 * 234 * <p>In the Connected state, this MAY indicate a mobility even occurred. 235 * 236 * @param arg1 The "all" token; this event is always applicable. 237 * @param obj @NonNull An EventUnderlyingNetworkChangedInfo instance with relevant data. 238 */ 239 private static final int EVENT_UNDERLYING_NETWORK_CHANGED = 1; 240 241 private static class EventUnderlyingNetworkChangedInfo implements EventInfo { 242 @Nullable public final UnderlyingNetworkRecord newUnderlying; 243 EventUnderlyingNetworkChangedInfo(@ullable UnderlyingNetworkRecord newUnderlying)244 EventUnderlyingNetworkChangedInfo(@Nullable UnderlyingNetworkRecord newUnderlying) { 245 this.newUnderlying = newUnderlying; 246 } 247 248 @Override hashCode()249 public int hashCode() { 250 return Objects.hash(newUnderlying); 251 } 252 253 @Override equals(@ullable Object other)254 public boolean equals(@Nullable Object other) { 255 if (!(other instanceof EventUnderlyingNetworkChangedInfo)) { 256 return false; 257 } 258 259 final EventUnderlyingNetworkChangedInfo rhs = (EventUnderlyingNetworkChangedInfo) other; 260 return Objects.equals(newUnderlying, rhs.newUnderlying); 261 } 262 } 263 264 /** 265 * Sent (delayed) to trigger an attempt to reestablish the tunnel. 266 * 267 * <p>Only relevant in the Retry-timeout state, discarded in all other states. 268 * 269 * <p>Upon receipt of this signal, the state machine will transition from the Retry-timeout 270 * state to the Connecting state. 271 * 272 * @param arg1 The "all" token; no sessions are active in the RetryTimeoutState. 273 */ 274 private static final int EVENT_RETRY_TIMEOUT_EXPIRED = 2; 275 276 /** 277 * Sent when a gateway connection has been lost, either due to a IKE or child failure. 278 * 279 * <p>Relevant in all states that have an IKE session. 280 * 281 * <p>Upon receipt of this signal, the state machine will (unless loss of the session is 282 * expected) transition to the Disconnecting state, to ensure IKE session closure before 283 * retrying, or fully shutting down. 284 * 285 * @param arg1 The session token for the IKE Session that was lost, used to prevent out-of-date 286 * signals from propagating. 287 * @param obj @NonNull An EventSessionLostInfo instance with relevant data. 288 */ 289 private static final int EVENT_SESSION_LOST = 3; 290 291 private static class EventSessionLostInfo implements EventInfo { 292 @Nullable public final Exception exception; 293 EventSessionLostInfo(@onNull Exception exception)294 EventSessionLostInfo(@NonNull Exception exception) { 295 this.exception = exception; 296 } 297 298 @Override hashCode()299 public int hashCode() { 300 return Objects.hash(exception); 301 } 302 303 @Override equals(@ullable Object other)304 public boolean equals(@Nullable Object other) { 305 if (!(other instanceof EventSessionLostInfo)) { 306 return false; 307 } 308 309 final EventSessionLostInfo rhs = (EventSessionLostInfo) other; 310 return Objects.equals(exception, rhs.exception); 311 } 312 } 313 314 /** 315 * Sent when an IKE session has completely closed. 316 * 317 * <p>Relevant only in the Disconnecting State, used to identify that a session being torn down 318 * was fully closed. If this event is not fired within a timely fashion, the IKE session will be 319 * forcibly terminated. 320 * 321 * <p>Upon receipt of this signal, the state machine will (unless closure of the session is 322 * expected) transition to the Disconnected or RetryTimeout states, depending on whether the 323 * GatewayConnection is being fully torn down. 324 * 325 * @param arg1 The session token for the IKE Session that was lost, used to prevent out-of-date 326 * signals from propagating. 327 * @param obj @NonNull An EventSessionLostInfo instance with relevant data. 328 */ 329 private static final int EVENT_SESSION_CLOSED = 4; 330 331 /** 332 * Sent when an IKE Child Transform was created, and should be applied to the tunnel. 333 * 334 * <p>Only relevant in the Connecting, Connected and Migrating states. This callback MUST be 335 * handled in the Connected or Migrating states, and should be deferred if necessary. 336 * 337 * @param arg1 The session token for the IKE Session that had a new child created, used to 338 * prevent out-of-date signals from propagating. 339 * @param obj @NonNull An EventTransformCreatedInfo instance with relevant data. 340 */ 341 private static final int EVENT_TRANSFORM_CREATED = 5; 342 343 private static class EventTransformCreatedInfo implements EventInfo { 344 @PolicyDirection public final int direction; 345 @NonNull public final IpSecTransform transform; 346 EventTransformCreatedInfo( @olicyDirection int direction, @NonNull IpSecTransform transform)347 EventTransformCreatedInfo( 348 @PolicyDirection int direction, @NonNull IpSecTransform transform) { 349 this.direction = direction; 350 this.transform = Objects.requireNonNull(transform); 351 } 352 353 @Override hashCode()354 public int hashCode() { 355 return Objects.hash(direction, transform); 356 } 357 358 @Override equals(@ullable Object other)359 public boolean equals(@Nullable Object other) { 360 if (!(other instanceof EventTransformCreatedInfo)) { 361 return false; 362 } 363 364 final EventTransformCreatedInfo rhs = (EventTransformCreatedInfo) other; 365 return direction == rhs.direction && Objects.equals(transform, rhs.transform); 366 } 367 } 368 369 /** 370 * Sent when an IKE Child Session was completely opened and configured successfully. 371 * 372 * <p>Only relevant in the Connected and Migrating states. 373 * 374 * @param arg1 The session token for the IKE Session for which a child was opened and configured 375 * successfully, used to prevent out-of-date signals from propagating. 376 * @param obj @NonNull An EventSetupCompletedInfo instance with relevant data. 377 */ 378 private static final int EVENT_SETUP_COMPLETED = 6; 379 380 private static class EventSetupCompletedInfo implements EventInfo { 381 @NonNull public final VcnChildSessionConfiguration childSessionConfig; 382 EventSetupCompletedInfo(@onNull VcnChildSessionConfiguration childSessionConfig)383 EventSetupCompletedInfo(@NonNull VcnChildSessionConfiguration childSessionConfig) { 384 this.childSessionConfig = Objects.requireNonNull(childSessionConfig); 385 } 386 387 @Override hashCode()388 public int hashCode() { 389 return Objects.hash(childSessionConfig); 390 } 391 392 @Override equals(@ullable Object other)393 public boolean equals(@Nullable Object other) { 394 if (!(other instanceof EventSetupCompletedInfo)) { 395 return false; 396 } 397 398 final EventSetupCompletedInfo rhs = (EventSetupCompletedInfo) other; 399 return Objects.equals(childSessionConfig, rhs.childSessionConfig); 400 } 401 } 402 403 /** 404 * Sent when conditions (internal or external) require a disconnect. 405 * 406 * <p>Relevant in all states except the Disconnected state. 407 * 408 * <p>This signal is often fired with a timeout in order to prevent disconnecting during 409 * transient conditions, such as network switches. Upon the transient passing, the signal is 410 * canceled based on the disconnect reason. 411 * 412 * <p>Upon receipt of this signal, the state machine MUST tear down all active sessions, cancel 413 * any pending work items, and move to the Disconnected state. 414 * 415 * @param arg1 The "all" token; this signal is always honored. 416 * @param obj @NonNull An EventDisconnectRequestedInfo instance with relevant data. 417 */ 418 private static final int EVENT_DISCONNECT_REQUESTED = 7; 419 420 private static class EventDisconnectRequestedInfo implements EventInfo { 421 /** The reason why the disconnect was requested. */ 422 @NonNull public final String reason; 423 424 public final boolean shouldQuit; 425 EventDisconnectRequestedInfo(@onNull String reason, boolean shouldQuit)426 EventDisconnectRequestedInfo(@NonNull String reason, boolean shouldQuit) { 427 this.reason = Objects.requireNonNull(reason); 428 this.shouldQuit = shouldQuit; 429 } 430 431 @Override hashCode()432 public int hashCode() { 433 return Objects.hash(reason, shouldQuit); 434 } 435 436 @Override equals(@ullable Object other)437 public boolean equals(@Nullable Object other) { 438 if (!(other instanceof EventDisconnectRequestedInfo)) { 439 return false; 440 } 441 442 final EventDisconnectRequestedInfo rhs = (EventDisconnectRequestedInfo) other; 443 return reason.equals(rhs.reason) && shouldQuit == rhs.shouldQuit; 444 } 445 } 446 447 /** 448 * Sent (delayed) to trigger a forcible close of an IKE session. 449 * 450 * <p>Only relevant in the Disconnecting state, discarded in all other states. 451 * 452 * <p>Upon receipt of this signal, the state machine will transition from the Disconnecting 453 * state to the Disconnected state. 454 * 455 * @param arg1 The session token for the IKE Session that is being torn down, used to prevent 456 * out-of-date signals from propagating. 457 */ 458 private static final int EVENT_TEARDOWN_TIMEOUT_EXPIRED = 8; 459 460 /** 461 * Sent when this VcnGatewayConnection is notified of a change in TelephonySubscriptions. 462 * 463 * <p>Relevant in all states. 464 * 465 * @param arg1 The "all" token; this signal is always honored. 466 */ 467 // TODO(b/178426520): implement handling of this event 468 private static final int EVENT_SUBSCRIPTIONS_CHANGED = 9; 469 470 /** 471 * Sent when this VcnGatewayConnection has entered safe mode. 472 * 473 * <p>A VcnGatewayConnection enters safe mode when it takes over {@link 474 * #SAFEMODE_TIMEOUT_SECONDS} to enter {@link ConnectedState}. 475 * 476 * <p>When a VcnGatewayConnection enters safe mode, it will fire {@link 477 * VcnGatewayStatusCallback#onEnteredSafeMode()} to notify its Vcn. The Vcn will then shut down 478 * its VcnGatewayConnectin(s). 479 * 480 * <p>Relevant in DisconnectingState, ConnectingState, ConnectedState (if the Vcn Network is not 481 * validated yet), and RetryTimeoutState. 482 * 483 * @param arg1 The "all" token; this signal is always honored. 484 */ 485 private static final int EVENT_SAFE_MODE_TIMEOUT_EXCEEDED = 10; 486 487 /** 488 * Sent when an IKE has completed migration, and created updated transforms for application. 489 * 490 * <p>Only relevant in the Connected state. 491 * 492 * @param arg1 The session token for the IKE Session that completed migration, used to prevent 493 * out-of-date signals from propagating. 494 * @param obj @NonNull An EventMigrationCompletedInfo instance with relevant data. 495 */ 496 private static final int EVENT_MIGRATION_COMPLETED = 11; 497 498 private static class EventMigrationCompletedInfo implements EventInfo { 499 @NonNull public final IpSecTransform inTransform; 500 @NonNull public final IpSecTransform outTransform; 501 EventMigrationCompletedInfo( @onNull IpSecTransform inTransform, @NonNull IpSecTransform outTransform)502 EventMigrationCompletedInfo( 503 @NonNull IpSecTransform inTransform, @NonNull IpSecTransform outTransform) { 504 this.inTransform = Objects.requireNonNull(inTransform); 505 this.outTransform = Objects.requireNonNull(outTransform); 506 } 507 508 @Override hashCode()509 public int hashCode() { 510 return Objects.hash(inTransform, outTransform); 511 } 512 513 @Override equals(@ullable Object other)514 public boolean equals(@Nullable Object other) { 515 if (!(other instanceof EventMigrationCompletedInfo)) { 516 return false; 517 } 518 519 final EventMigrationCompletedInfo rhs = (EventMigrationCompletedInfo) other; 520 return Objects.equals(inTransform, rhs.inTransform) 521 && Objects.equals(outTransform, rhs.outTransform); 522 } 523 } 524 525 /** 526 * Sent when an IKE session connection information has changed. 527 * 528 * <p>This signal is always fired before EVENT_SETUP_COMPLETED and EVENT_MIGRATION_COMPLETED. 529 * 530 * <p>Only relevant in the Connecting and Connected state. 531 * 532 * @param arg1 The session token for the IKE Session whose connection information has changed, 533 * used to prevent out-of-date signals from propagating. 534 * @param obj @NonNull An EventIkeConnectionInfoChangedInfo instance with relevant data. 535 */ 536 private static final int EVENT_IKE_CONNECTION_INFO_CHANGED = 12; 537 538 private static class EventIkeConnectionInfoChangedInfo implements EventInfo { 539 @NonNull public final IkeSessionConnectionInfo ikeConnectionInfo; 540 EventIkeConnectionInfoChangedInfo(@onNull IkeSessionConnectionInfo ikeConnectionInfo)541 EventIkeConnectionInfoChangedInfo(@NonNull IkeSessionConnectionInfo ikeConnectionInfo) { 542 this.ikeConnectionInfo = ikeConnectionInfo; 543 } 544 545 @Override hashCode()546 public int hashCode() { 547 return Objects.hash(ikeConnectionInfo); 548 } 549 550 @Override equals(@ullable Object other)551 public boolean equals(@Nullable Object other) { 552 if (!(other instanceof EventIkeConnectionInfoChangedInfo)) { 553 return false; 554 } 555 556 final EventIkeConnectionInfoChangedInfo rhs = (EventIkeConnectionInfoChangedInfo) other; 557 return Objects.equals(ikeConnectionInfo, rhs.ikeConnectionInfo); 558 } 559 } 560 561 /** 562 * Sent when there is a suspected data stall on a network 563 * 564 * <p>Only relevant in the Connected state. 565 * 566 * @param arg1 The "all" token; this signal is always honored. 567 * @param obj @NonNull An EventDataStallSuspectedInfo instance with relevant data. 568 */ 569 private static final int EVENT_DATA_STALL_SUSPECTED = 13; 570 571 private static class EventDataStallSuspectedInfo implements EventInfo { 572 @NonNull public final Network network; 573 EventDataStallSuspectedInfo(@onNull Network network)574 EventDataStallSuspectedInfo(@NonNull Network network) { 575 this.network = network; 576 } 577 578 @Override hashCode()579 public int hashCode() { 580 return Objects.hash(network); 581 } 582 583 @Override equals(@ullable Object other)584 public boolean equals(@Nullable Object other) { 585 if (!(other instanceof EventDataStallSuspectedInfo)) { 586 return false; 587 } 588 589 final EventDataStallSuspectedInfo rhs = (EventDataStallSuspectedInfo) other; 590 return Objects.equals(network, rhs.network); 591 } 592 } 593 594 @VisibleForTesting(visibility = Visibility.PRIVATE) 595 @NonNull 596 final DisconnectedState mDisconnectedState = new DisconnectedState(); 597 598 @VisibleForTesting(visibility = Visibility.PRIVATE) 599 @NonNull 600 final DisconnectingState mDisconnectingState = new DisconnectingState(); 601 602 @VisibleForTesting(visibility = Visibility.PRIVATE) 603 @NonNull 604 final ConnectingState mConnectingState = new ConnectingState(); 605 606 @VisibleForTesting(visibility = Visibility.PRIVATE) 607 @NonNull 608 final ConnectedState mConnectedState = new ConnectedState(); 609 610 @VisibleForTesting(visibility = Visibility.PRIVATE) 611 @NonNull 612 final RetryTimeoutState mRetryTimeoutState = new RetryTimeoutState(); 613 614 @NonNull private TelephonySubscriptionSnapshot mLastSnapshot; 615 616 @NonNull private final VcnContext mVcnContext; 617 @NonNull private final ParcelUuid mSubscriptionGroup; 618 @NonNull private final UnderlyingNetworkController mUnderlyingNetworkController; 619 @NonNull private final VcnGatewayConnectionConfig mConnectionConfig; 620 @NonNull private final VcnGatewayStatusCallback mGatewayStatusCallback; 621 @NonNull private final Dependencies mDeps; 622 623 @NonNull 624 private final VcnUnderlyingNetworkControllerCallback mUnderlyingNetworkControllerCallback; 625 626 @NonNull private final VcnConnectivityDiagnosticsCallback mConnectivityDiagnosticsCallback; 627 628 private final boolean mIsMobileDataEnabled; 629 630 @NonNull private final IpSecManager mIpSecManager; 631 @NonNull private final ConnectivityManager mConnectivityManager; 632 @NonNull private final ConnectivityDiagnosticsManager mConnectivityDiagnosticsManager; 633 634 @Nullable private IpSecTunnelInterface mTunnelIface = null; 635 636 /** 637 * WakeLock to be held when processing messages on the Handler queue. 638 * 639 * <p>Used to prevent the device from going to sleep while there are VCN-related events to 640 * process for this VcnGatewayConnection. 641 * 642 * <p>Obtain a WakeLock when enquing messages onto the Handler queue. Once all messages in the 643 * Handler queue have been processed, the WakeLock can be released and cleared. 644 * 645 * <p>This WakeLock is also used for handling delayed messages by using WakeupMessages to send 646 * delayed messages to the Handler. When the WakeupMessage fires, it will obtain the WakeLock 647 * before enquing the delayed event to the Handler. 648 */ 649 @NonNull private final VcnWakeLock mWakeLock; 650 651 /** 652 * Whether the VcnGatewayConnection is in the process of irreversibly quitting. 653 * 654 * <p>This variable is false for the lifecycle of the VcnGatewayConnection, until a command to 655 * teardown has been received. This may be flipped due to events such as the Network becoming 656 * unwanted, the owning VCN entering safe mode, or an irrecoverable internal failure. 657 * 658 * <p>WARNING: Assignments to this MUST ALWAYS (except for testing) use the or operator ("|="), 659 * otherwise the flag may be flipped back to false after having been set to true. This could 660 * lead to a case where the Vcn parent instance has commanded a teardown, but a spurious 661 * non-quitting disconnect request could flip this back to true. 662 */ 663 private OneWayBoolean mIsQuitting = new OneWayBoolean(); 664 665 /** 666 * Whether the VcnGatewayConnection is in safe mode. 667 * 668 * <p>Upon hitting the safe mode timeout, this will be set to {@code true}. In safe mode, this 669 * VcnGatewayConnection will continue attempting to connect, and if a successful connection is 670 * made, safe mode will be exited. 671 */ 672 private boolean mIsInSafeMode = false; 673 674 /** 675 * The token used by the primary/current/active session. 676 * 677 * <p>This token MUST be updated when a new stateful/async session becomes the 678 * primary/current/active session. Example cases where the session changes are: 679 * 680 * <ul> 681 * <li>Switching to an IKE session as the primary session 682 * </ul> 683 * 684 * <p>In the migrating state, where two sessions may be active, this value MUST represent the 685 * primary session. This is USUALLY the existing session, and is only switched to the new 686 * session when: 687 * 688 * <ul> 689 * <li>The new session connects successfully, and becomes the primary session 690 * <li>The existing session is lost, and the remaining (new) session becomes the primary 691 * session 692 * </ul> 693 */ 694 private int mCurrentToken = -1; 695 696 /** 697 * The number of unsuccessful attempts since the last successful connection. 698 * 699 * <p>This number MUST be incremented each time the RetryTimeout state is entered, and cleared 700 * each time the Connected state is entered. 701 */ 702 private int mFailedAttempts = 0; 703 704 /** 705 * The current underlying network. 706 * 707 * <p>Set in any states, always @NonNull in all states except Disconnected, null otherwise. 708 */ 709 private UnderlyingNetworkRecord mUnderlying; 710 711 /** 712 * The current IKE Session connection information 713 * 714 * <p>Set in Connected and Migrating states, always @NonNull in Connected, Migrating 715 * states, @Nullable otherwise. 716 */ 717 private IkeSessionConnectionInfo mIkeConnectionInfo; 718 719 /** 720 * The active IKE session. 721 * 722 * <p>Set in Connecting or Migrating States, always @NonNull in Connecting, Connected, and 723 * Migrating states, null otherwise. 724 */ 725 private VcnIkeSession mIkeSession; 726 727 /** 728 * The last known child configuration. 729 * 730 * <p>Set in Connected and Migrating states, always @NonNull in Connected, Migrating 731 * states, @Nullable otherwise. 732 */ 733 private VcnChildSessionConfiguration mChildConfig; 734 735 /** 736 * The active network agent. 737 * 738 * <p>Set in Connected state, always @NonNull in Connected, Migrating states, @Nullable 739 * otherwise. 740 */ 741 private VcnNetworkAgent mNetworkAgent; 742 743 @Nullable private WakeupMessage mTeardownTimeoutAlarm; 744 @Nullable private WakeupMessage mDisconnectRequestAlarm; 745 @Nullable private WakeupMessage mRetryTimeoutAlarm; 746 @Nullable private WakeupMessage mSafeModeTimeoutAlarm; 747 VcnGatewayConnection( @onNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull VcnGatewayConnectionConfig connectionConfig, @NonNull VcnGatewayStatusCallback gatewayStatusCallback, boolean isMobileDataEnabled)748 public VcnGatewayConnection( 749 @NonNull VcnContext vcnContext, 750 @NonNull ParcelUuid subscriptionGroup, 751 @NonNull TelephonySubscriptionSnapshot snapshot, 752 @NonNull VcnGatewayConnectionConfig connectionConfig, 753 @NonNull VcnGatewayStatusCallback gatewayStatusCallback, 754 boolean isMobileDataEnabled) { 755 this( 756 vcnContext, 757 subscriptionGroup, 758 snapshot, 759 connectionConfig, 760 gatewayStatusCallback, 761 isMobileDataEnabled, 762 new Dependencies()); 763 } 764 765 @VisibleForTesting(visibility = Visibility.PRIVATE) VcnGatewayConnection( @onNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull VcnGatewayConnectionConfig connectionConfig, @NonNull VcnGatewayStatusCallback gatewayStatusCallback, boolean isMobileDataEnabled, @NonNull Dependencies deps)766 VcnGatewayConnection( 767 @NonNull VcnContext vcnContext, 768 @NonNull ParcelUuid subscriptionGroup, 769 @NonNull TelephonySubscriptionSnapshot snapshot, 770 @NonNull VcnGatewayConnectionConfig connectionConfig, 771 @NonNull VcnGatewayStatusCallback gatewayStatusCallback, 772 boolean isMobileDataEnabled, 773 @NonNull Dependencies deps) { 774 super(TAG, Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper()); 775 mVcnContext = vcnContext; 776 mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup"); 777 mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig"); 778 mGatewayStatusCallback = 779 Objects.requireNonNull(gatewayStatusCallback, "Missing gatewayStatusCallback"); 780 mIsMobileDataEnabled = isMobileDataEnabled; 781 mDeps = Objects.requireNonNull(deps, "Missing deps"); 782 783 mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot"); 784 785 mUnderlyingNetworkControllerCallback = new VcnUnderlyingNetworkControllerCallback(); 786 787 mWakeLock = 788 mDeps.newWakeLock(mVcnContext.getContext(), PowerManager.PARTIAL_WAKE_LOCK, TAG); 789 790 mUnderlyingNetworkController = 791 mDeps.newUnderlyingNetworkController( 792 mVcnContext, 793 mConnectionConfig, 794 subscriptionGroup, 795 mLastSnapshot, 796 mUnderlyingNetworkControllerCallback); 797 mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class); 798 mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class); 799 mConnectivityDiagnosticsManager = 800 mVcnContext.getContext().getSystemService(ConnectivityDiagnosticsManager.class); 801 802 mConnectivityDiagnosticsCallback = new VcnConnectivityDiagnosticsCallback(); 803 804 if (mConnectionConfig.hasGatewayOption( 805 VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY)) { 806 final NetworkRequest diagRequest = 807 new NetworkRequest.Builder().addTransportType(TRANSPORT_CELLULAR).build(); 808 mConnectivityDiagnosticsManager.registerConnectivityDiagnosticsCallback( 809 diagRequest, 810 new HandlerExecutor(new Handler(vcnContext.getLooper())), 811 mConnectivityDiagnosticsCallback); 812 } 813 814 addState(mDisconnectedState); 815 addState(mDisconnectingState); 816 addState(mConnectingState); 817 addState(mConnectedState); 818 addState(mRetryTimeoutState); 819 820 setInitialState(mDisconnectedState); 821 setDbg(VDBG); 822 start(); 823 } 824 825 /** Queries whether this VcnGatewayConnection is in safe mode. */ isInSafeMode()826 public boolean isInSafeMode() { 827 // Accessing internal state; must only be done on looper thread. 828 mVcnContext.ensureRunningOnLooperThread(); 829 830 return mIsInSafeMode; 831 } 832 833 /** 834 * Asynchronously tears down this GatewayConnection, and any resources used. 835 * 836 * <p>Once torn down, this VcnTunnel CANNOT be started again. 837 */ teardownAsynchronously()838 public void teardownAsynchronously() { 839 logDbg("Triggering async teardown"); 840 sendDisconnectRequestedAndAcquireWakelock( 841 DISCONNECT_REASON_TEARDOWN, true /* shouldQuit */); 842 } 843 844 @Override onQuitting()845 protected void onQuitting() { 846 logInfo("Quitting VcnGatewayConnection"); 847 848 if (mNetworkAgent != null) { 849 logWtf("NetworkAgent was non-null in onQuitting"); 850 mNetworkAgent.unregister(); 851 mNetworkAgent = null; 852 } 853 854 if (mIkeSession != null) { 855 logWtf("IkeSession was non-null in onQuitting"); 856 mIkeSession.kill(); 857 mIkeSession = null; 858 } 859 860 // No need to call setInterfaceDown(); the IpSecInterface is being fully torn down. 861 if (mTunnelIface != null) { 862 mTunnelIface.close(); 863 } 864 865 releaseWakeLock(); 866 867 cancelTeardownTimeoutAlarm(); 868 cancelDisconnectRequestAlarm(); 869 cancelRetryTimeoutAlarm(); 870 cancelSafeModeAlarm(); 871 872 mUnderlyingNetworkController.teardown(); 873 874 mGatewayStatusCallback.onQuit(); 875 876 mConnectivityDiagnosticsManager.unregisterConnectivityDiagnosticsCallback( 877 mConnectivityDiagnosticsCallback); 878 } 879 880 /** 881 * Notify this Gateway that subscriptions have changed. 882 * 883 * <p>This snapshot should be used to update any keepalive requests necessary for potential 884 * underlying Networks in this Gateway's subscription group. 885 */ updateSubscriptionSnapshot(@onNull TelephonySubscriptionSnapshot snapshot)886 public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) { 887 Objects.requireNonNull(snapshot, "Missing snapshot"); 888 mVcnContext.ensureRunningOnLooperThread(); 889 890 mLastSnapshot = snapshot; 891 mUnderlyingNetworkController.updateSubscriptionSnapshot(mLastSnapshot); 892 893 sendMessageAndAcquireWakeLock(EVENT_SUBSCRIPTIONS_CHANGED, TOKEN_ALL); 894 } 895 896 private class VcnConnectivityDiagnosticsCallback extends ConnectivityDiagnosticsCallback { 897 @Override onDataStallSuspected(ConnectivityDiagnosticsManager.DataStallReport report)898 public void onDataStallSuspected(ConnectivityDiagnosticsManager.DataStallReport report) { 899 mVcnContext.ensureRunningOnLooperThread(); 900 901 final Network network = report.getNetwork(); 902 logInfo("Data stall suspected on " + network); 903 sendMessageAndAcquireWakeLock( 904 EVENT_DATA_STALL_SUSPECTED, 905 TOKEN_ALL, 906 new EventDataStallSuspectedInfo(network)); 907 } 908 } 909 910 private class VcnUnderlyingNetworkControllerCallback 911 implements UnderlyingNetworkControllerCallback { 912 @Override onSelectedUnderlyingNetworkChanged( @ullable UnderlyingNetworkRecord underlying)913 public void onSelectedUnderlyingNetworkChanged( 914 @Nullable UnderlyingNetworkRecord underlying) { 915 // TODO(b/180132994): explore safely removing this Thread check 916 mVcnContext.ensureRunningOnLooperThread(); 917 918 logInfo( 919 "Selected underlying network changed: " 920 + (underlying == null ? null : underlying.network)); 921 922 // TODO(b/179091925): Move the delayed-message handling to BaseState 923 924 // If underlying is null, all underlying networks have been lost. Disconnect VCN after a 925 // timeout (or immediately if in airplane mode, since the device user has indicated that 926 // the radios should all be turned off). 927 if (underlying == null) { 928 if (mDeps.isAirplaneModeOn(mVcnContext)) { 929 sendMessageAndAcquireWakeLock( 930 EVENT_UNDERLYING_NETWORK_CHANGED, 931 TOKEN_ALL, 932 new EventUnderlyingNetworkChangedInfo(null)); 933 sendDisconnectRequestedAndAcquireWakelock( 934 DISCONNECT_REASON_UNDERLYING_NETWORK_LOST, false /* shouldQuit */); 935 return; 936 } 937 938 setDisconnectRequestAlarm(); 939 } else { 940 // Received a new Network so any previous alarm is irrelevant - cancel + clear it, 941 // and cancel any queued EVENT_DISCONNECT_REQUEST messages 942 cancelDisconnectRequestAlarm(); 943 } 944 945 sendMessageAndAcquireWakeLock( 946 EVENT_UNDERLYING_NETWORK_CHANGED, 947 TOKEN_ALL, 948 new EventUnderlyingNetworkChangedInfo(underlying)); 949 } 950 } 951 acquireWakeLock()952 private void acquireWakeLock() { 953 mVcnContext.ensureRunningOnLooperThread(); 954 955 if (!mIsQuitting.getValue()) { 956 mWakeLock.acquire(); 957 958 logVdbg("Wakelock acquired: " + mWakeLock); 959 } 960 } 961 releaseWakeLock()962 private void releaseWakeLock() { 963 mVcnContext.ensureRunningOnLooperThread(); 964 965 mWakeLock.release(); 966 967 logVdbg("Wakelock released: " + mWakeLock); 968 } 969 970 /** 971 * Attempt to release mWakeLock - this can only be done if the Handler is null (meaning the 972 * StateMachine has been shutdown and thus has no business keeping the WakeLock) or if there are 973 * no more messags left to process in the Handler queue (at which point the WakeLock can be 974 * released until more messages must be processed). 975 */ maybeReleaseWakeLock()976 private void maybeReleaseWakeLock() { 977 final Handler handler = getHandler(); 978 if (handler == null || !handler.hasMessagesOrCallbacks()) { 979 releaseWakeLock(); 980 } 981 } 982 983 @Override sendMessage(int what)984 public void sendMessage(int what) { 985 logWtf( 986 "sendMessage should not be used in VcnGatewayConnection. See" 987 + " sendMessageAndAcquireWakeLock()"); 988 super.sendMessage(what); 989 } 990 991 @Override sendMessage(int what, Object obj)992 public void sendMessage(int what, Object obj) { 993 logWtf( 994 "sendMessage should not be used in VcnGatewayConnection. See" 995 + " sendMessageAndAcquireWakeLock()"); 996 super.sendMessage(what, obj); 997 } 998 999 @Override sendMessage(int what, int arg1)1000 public void sendMessage(int what, int arg1) { 1001 logWtf( 1002 "sendMessage should not be used in VcnGatewayConnection. See" 1003 + " sendMessageAndAcquireWakeLock()"); 1004 super.sendMessage(what, arg1); 1005 } 1006 1007 @Override sendMessage(int what, int arg1, int arg2)1008 public void sendMessage(int what, int arg1, int arg2) { 1009 logWtf( 1010 "sendMessage should not be used in VcnGatewayConnection. See" 1011 + " sendMessageAndAcquireWakeLock()"); 1012 super.sendMessage(what, arg1, arg2); 1013 } 1014 1015 @Override sendMessage(int what, int arg1, int arg2, Object obj)1016 public void sendMessage(int what, int arg1, int arg2, Object obj) { 1017 logWtf( 1018 "sendMessage should not be used in VcnGatewayConnection. See" 1019 + " sendMessageAndAcquireWakeLock()"); 1020 super.sendMessage(what, arg1, arg2, obj); 1021 } 1022 1023 @Override sendMessage(Message msg)1024 public void sendMessage(Message msg) { 1025 logWtf( 1026 "sendMessage should not be used in VcnGatewayConnection. See" 1027 + " sendMessageAndAcquireWakeLock()"); 1028 super.sendMessage(msg); 1029 } 1030 1031 // TODO(b/180146061): also override and Log.wtf() other Message handling methods 1032 // In mind are sendMessageDelayed(), sendMessageAtFrontOfQueue, removeMessages, and 1033 // removeDeferredMessages 1034 1035 /** 1036 * WakeLock-based alternative to {@link #sendMessage}. Use to guarantee that the device will not 1037 * go to sleep before processing the sent message. 1038 */ sendMessageAndAcquireWakeLock(int what, int token)1039 private void sendMessageAndAcquireWakeLock(int what, int token) { 1040 acquireWakeLock(); 1041 super.sendMessage(what, token); 1042 } 1043 1044 /** 1045 * WakeLock-based alternative to {@link #sendMessage}. Use to guarantee that the device will not 1046 * go to sleep before processing the sent message. 1047 */ sendMessageAndAcquireWakeLock(int what, int token, EventInfo data)1048 private void sendMessageAndAcquireWakeLock(int what, int token, EventInfo data) { 1049 acquireWakeLock(); 1050 super.sendMessage(what, token, ARG_NOT_PRESENT, data); 1051 } 1052 1053 /** 1054 * WakeLock-based alternative to {@link #sendMessage}. Use to guarantee that the device will not 1055 * go to sleep before processing the sent message. 1056 */ sendMessageAndAcquireWakeLock(int what, int token, int arg2, EventInfo data)1057 private void sendMessageAndAcquireWakeLock(int what, int token, int arg2, EventInfo data) { 1058 acquireWakeLock(); 1059 super.sendMessage(what, token, arg2, data); 1060 } 1061 1062 /** 1063 * WakeLock-based alternative to {@link #sendMessage}. Use to guarantee that the device will not 1064 * go to sleep before processing the sent message. 1065 */ sendMessageAndAcquireWakeLock(Message msg)1066 private void sendMessageAndAcquireWakeLock(Message msg) { 1067 acquireWakeLock(); 1068 super.sendMessage(msg); 1069 } 1070 1071 /** 1072 * Removes all messages matching the given parameters, and attempts to release mWakeLock if the 1073 * Handler is empty. 1074 * 1075 * @param what the Message.what value to be removed 1076 */ removeEqualMessages(int what)1077 private void removeEqualMessages(int what) { 1078 removeEqualMessages(what, null /* obj */); 1079 } 1080 1081 /** 1082 * Removes all messages matching the given parameters, and attempts to release mWakeLock if the 1083 * Handler is empty. 1084 * 1085 * @param what the Message.what value to be removed 1086 * @param obj the Message.obj to to be removed, or null if all messages matching Message.what 1087 * should be removed 1088 */ removeEqualMessages(int what, @Nullable Object obj)1089 private void removeEqualMessages(int what, @Nullable Object obj) { 1090 final Handler handler = getHandler(); 1091 if (handler != null) { 1092 handler.removeEqualMessages(what, obj); 1093 } 1094 1095 maybeReleaseWakeLock(); 1096 } 1097 createScheduledAlarm( @onNull String cmdName, Message delayedMessage, long delay)1098 private WakeupMessage createScheduledAlarm( 1099 @NonNull String cmdName, Message delayedMessage, long delay) { 1100 final Handler handler = getHandler(); 1101 if (handler == null) { 1102 logWarn( 1103 "Attempted to schedule alarm after StateMachine has quit", 1104 new IllegalStateException()); 1105 return null; // StateMachine has already quit. 1106 } 1107 1108 // WakeupMessage uses Handler#dispatchMessage() to immediately handle the specified Runnable 1109 // at the scheduled time. dispatchMessage() immediately executes and there may be queued 1110 // events that resolve the scheduled alarm pending in the queue. So, use the Runnable to 1111 // place the alarm event at the end of the queue with sendMessageAndAcquireWakeLock (which 1112 // guarantees the device will stay awake). 1113 final WakeupMessage alarm = 1114 mDeps.newWakeupMessage( 1115 mVcnContext, 1116 handler, 1117 cmdName, 1118 () -> sendMessageAndAcquireWakeLock(delayedMessage)); 1119 alarm.schedule(mDeps.getElapsedRealTime() + delay); 1120 return alarm; 1121 } 1122 setTeardownTimeoutAlarm()1123 private void setTeardownTimeoutAlarm() { 1124 logVdbg("Setting teardown timeout alarm; mCurrentToken: " + mCurrentToken); 1125 1126 // Safe to assign this alarm because it is either 1) already null, or 2) already fired. In 1127 // either case, there is nothing to cancel. 1128 if (mTeardownTimeoutAlarm != null) { 1129 logWtf( 1130 "mTeardownTimeoutAlarm should be null before being set; mCurrentToken: " 1131 + mCurrentToken); 1132 } 1133 1134 final Message delayedMessage = obtainMessage(EVENT_TEARDOWN_TIMEOUT_EXPIRED, mCurrentToken); 1135 mTeardownTimeoutAlarm = 1136 createScheduledAlarm( 1137 TEARDOWN_TIMEOUT_ALARM, 1138 delayedMessage, 1139 TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS)); 1140 } 1141 cancelTeardownTimeoutAlarm()1142 private void cancelTeardownTimeoutAlarm() { 1143 logVdbg("Cancelling teardown timeout alarm; mCurrentToken: " + mCurrentToken); 1144 1145 if (mTeardownTimeoutAlarm != null) { 1146 mTeardownTimeoutAlarm.cancel(); 1147 mTeardownTimeoutAlarm = null; 1148 } 1149 1150 // Cancel any existing teardown timeouts 1151 removeEqualMessages(EVENT_TEARDOWN_TIMEOUT_EXPIRED); 1152 } 1153 setDisconnectRequestAlarm()1154 private void setDisconnectRequestAlarm() { 1155 logVdbg( 1156 "Setting alarm to disconnect due to underlying network loss;" 1157 + " mCurrentToken: " 1158 + mCurrentToken); 1159 1160 // Only schedule a NEW alarm if none is already set. 1161 if (mDisconnectRequestAlarm != null) { 1162 return; 1163 } 1164 1165 final Message delayedMessage = 1166 obtainMessage( 1167 EVENT_DISCONNECT_REQUESTED, 1168 TOKEN_ALL, 1169 0 /* arg2 */, 1170 new EventDisconnectRequestedInfo( 1171 DISCONNECT_REASON_UNDERLYING_NETWORK_LOST, false /* shouldQuit */)); 1172 mDisconnectRequestAlarm = 1173 createScheduledAlarm( 1174 DISCONNECT_REQUEST_ALARM, 1175 delayedMessage, 1176 TimeUnit.SECONDS.toMillis(NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS)); 1177 } 1178 cancelDisconnectRequestAlarm()1179 private void cancelDisconnectRequestAlarm() { 1180 logVdbg( 1181 "Cancelling alarm to disconnect due to underlying network loss;" 1182 + " mCurrentToken: " 1183 + mCurrentToken); 1184 1185 if (mDisconnectRequestAlarm != null) { 1186 mDisconnectRequestAlarm.cancel(); 1187 mDisconnectRequestAlarm = null; 1188 } 1189 1190 // Cancel any existing disconnect due to previous loss of underlying network 1191 removeEqualMessages( 1192 EVENT_DISCONNECT_REQUESTED, 1193 new EventDisconnectRequestedInfo( 1194 DISCONNECT_REASON_UNDERLYING_NETWORK_LOST, false /* shouldQuit */)); 1195 } 1196 setRetryTimeoutAlarm(long delay)1197 private void setRetryTimeoutAlarm(long delay) { 1198 logVdbg("Setting retry alarm; mCurrentToken: " + mCurrentToken); 1199 1200 // Safe to assign this alarm because it is either 1) already null, or 2) already fired. In 1201 // either case, there is nothing to cancel. 1202 if (mRetryTimeoutAlarm != null) { 1203 logWtf( 1204 "mRetryTimeoutAlarm should be null before being set; mCurrentToken: " 1205 + mCurrentToken); 1206 } 1207 1208 final Message delayedMessage = obtainMessage(EVENT_RETRY_TIMEOUT_EXPIRED, mCurrentToken); 1209 mRetryTimeoutAlarm = createScheduledAlarm(RETRY_TIMEOUT_ALARM, delayedMessage, delay); 1210 } 1211 cancelRetryTimeoutAlarm()1212 private void cancelRetryTimeoutAlarm() { 1213 logVdbg("Cancel retry alarm; mCurrentToken: " + mCurrentToken); 1214 1215 if (mRetryTimeoutAlarm != null) { 1216 mRetryTimeoutAlarm.cancel(); 1217 mRetryTimeoutAlarm = null; 1218 } 1219 1220 removeEqualMessages(EVENT_RETRY_TIMEOUT_EXPIRED); 1221 } 1222 1223 @VisibleForTesting(visibility = Visibility.PRIVATE) setSafeModeAlarm()1224 void setSafeModeAlarm() { 1225 logVdbg("Setting safe mode alarm; mCurrentToken: " + mCurrentToken); 1226 1227 // Only schedule a NEW alarm if none is already set. 1228 if (mSafeModeTimeoutAlarm != null) { 1229 return; 1230 } 1231 1232 final Message delayedMessage = obtainMessage(EVENT_SAFE_MODE_TIMEOUT_EXCEEDED, TOKEN_ALL); 1233 mSafeModeTimeoutAlarm = 1234 createScheduledAlarm( 1235 SAFEMODE_TIMEOUT_ALARM, 1236 delayedMessage, 1237 mVcnContext.isInTestMode() 1238 ? TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS_TEST_MODE) 1239 : TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS)); 1240 } 1241 cancelSafeModeAlarm()1242 private void cancelSafeModeAlarm() { 1243 logVdbg("Cancel safe mode alarm; mCurrentToken: " + mCurrentToken); 1244 1245 if (mSafeModeTimeoutAlarm != null) { 1246 mSafeModeTimeoutAlarm.cancel(); 1247 mSafeModeTimeoutAlarm = null; 1248 } 1249 1250 removeEqualMessages(EVENT_SAFE_MODE_TIMEOUT_EXCEEDED); 1251 } 1252 sessionLostWithoutCallback(int token, @Nullable Exception exception)1253 private void sessionLostWithoutCallback(int token, @Nullable Exception exception) { 1254 sendMessageAndAcquireWakeLock( 1255 EVENT_SESSION_LOST, token, new EventSessionLostInfo(exception)); 1256 } 1257 sessionLost(int token, @Nullable Exception exception)1258 private void sessionLost(int token, @Nullable Exception exception) { 1259 // Only notify mGatewayStatusCallback if the session was lost with an error. All 1260 // authentication and DNS failures are sent through 1261 // IkeSessionCallback.onClosedExceptionally(), which calls sessionClosed() 1262 if (exception != null) { 1263 mGatewayStatusCallback.onGatewayConnectionError( 1264 mConnectionConfig.getGatewayConnectionName(), 1265 VCN_ERROR_CODE_INTERNAL_ERROR, 1266 RuntimeException.class.getName(), 1267 "Received " 1268 + exception.getClass().getSimpleName() 1269 + " with message: " 1270 + exception.getMessage()); 1271 } 1272 1273 sessionLostWithoutCallback(token, exception); 1274 } 1275 isIkeAuthFailure(@onNull Exception exception)1276 private static boolean isIkeAuthFailure(@NonNull Exception exception) { 1277 if (!(exception instanceof IkeProtocolException)) { 1278 return false; 1279 } 1280 1281 return ((IkeProtocolException) exception).getErrorType() 1282 == ERROR_TYPE_AUTHENTICATION_FAILED; 1283 } 1284 notifyStatusCallbackForSessionClosed(@onNull Exception exception)1285 private void notifyStatusCallbackForSessionClosed(@NonNull Exception exception) { 1286 final int errorCode; 1287 final String exceptionClass; 1288 final String exceptionMessage; 1289 1290 if (isIkeAuthFailure(exception)) { 1291 errorCode = VCN_ERROR_CODE_CONFIG_ERROR; 1292 exceptionClass = exception.getClass().getName(); 1293 exceptionMessage = exception.getMessage(); 1294 } else if (exception instanceof IkeInternalException 1295 && exception.getCause() instanceof IOException) { 1296 errorCode = VCN_ERROR_CODE_NETWORK_ERROR; 1297 exceptionClass = IOException.class.getName(); 1298 exceptionMessage = exception.getCause().getMessage(); 1299 } else { 1300 errorCode = VCN_ERROR_CODE_INTERNAL_ERROR; 1301 exceptionClass = RuntimeException.class.getName(); 1302 exceptionMessage = 1303 "Received " 1304 + exception.getClass().getSimpleName() 1305 + " with message: " 1306 + exception.getMessage(); 1307 } 1308 1309 logDbg( 1310 "Encountered error; code=" 1311 + errorCode 1312 + ", exceptionClass=" 1313 + exceptionClass 1314 + ", exceptionMessage=" 1315 + exceptionMessage); 1316 1317 mGatewayStatusCallback.onGatewayConnectionError( 1318 mConnectionConfig.getGatewayConnectionName(), 1319 errorCode, 1320 exceptionClass, 1321 exceptionMessage); 1322 } 1323 ikeConnectionInfoChanged( int token, @NonNull IkeSessionConnectionInfo ikeConnectionInfo)1324 private void ikeConnectionInfoChanged( 1325 int token, @NonNull IkeSessionConnectionInfo ikeConnectionInfo) { 1326 sendMessageAndAcquireWakeLock( 1327 EVENT_IKE_CONNECTION_INFO_CHANGED, 1328 token, 1329 new EventIkeConnectionInfoChangedInfo(ikeConnectionInfo)); 1330 } 1331 sessionClosed(int token, @Nullable Exception exception)1332 private void sessionClosed(int token, @Nullable Exception exception) { 1333 if (exception != null) { 1334 notifyStatusCallbackForSessionClosed(exception); 1335 } 1336 1337 // SESSION_LOST MUST be sent before SESSION_CLOSED to ensure that the SM moves to the 1338 // Disconnecting state. 1339 sessionLostWithoutCallback(token, exception); 1340 sendMessageAndAcquireWakeLock(EVENT_SESSION_CLOSED, token); 1341 } 1342 migrationCompleted( int token, @NonNull IpSecTransform inTransform, @NonNull IpSecTransform outTransform)1343 private void migrationCompleted( 1344 int token, @NonNull IpSecTransform inTransform, @NonNull IpSecTransform outTransform) { 1345 sendMessageAndAcquireWakeLock( 1346 EVENT_MIGRATION_COMPLETED, 1347 token, 1348 new EventMigrationCompletedInfo(inTransform, outTransform)); 1349 } 1350 childTransformCreated( int token, @NonNull IpSecTransform transform, int direction)1351 private void childTransformCreated( 1352 int token, @NonNull IpSecTransform transform, int direction) { 1353 sendMessageAndAcquireWakeLock( 1354 EVENT_TRANSFORM_CREATED, 1355 token, 1356 new EventTransformCreatedInfo(direction, transform)); 1357 } 1358 childOpened(int token, @NonNull VcnChildSessionConfiguration childConfig)1359 private void childOpened(int token, @NonNull VcnChildSessionConfiguration childConfig) { 1360 sendMessageAndAcquireWakeLock( 1361 EVENT_SETUP_COMPLETED, token, new EventSetupCompletedInfo(childConfig)); 1362 } 1363 1364 private abstract class BaseState extends State { 1365 @Override enter()1366 public void enter() { 1367 try { 1368 enterState(); 1369 } catch (Exception e) { 1370 logWtf("Uncaught exception", e); 1371 sendDisconnectRequestedAndAcquireWakelock( 1372 DISCONNECT_REASON_INTERNAL_ERROR + e.toString(), true /* shouldQuit */); 1373 } 1374 } 1375 enterState()1376 protected void enterState() throws Exception {} 1377 1378 /** 1379 * Returns whether the given token is valid. 1380 * 1381 * <p>By default, States consider any and all token to be 'valid'. 1382 * 1383 * <p>States should override this method if they want to restrict message handling to 1384 * specific tokens. 1385 */ isValidToken(int token)1386 protected boolean isValidToken(int token) { 1387 return true; 1388 } 1389 1390 /** 1391 * Top-level processMessage with safeguards to prevent crashing the System Server on non-eng 1392 * builds. 1393 * 1394 * <p>Here be dragons: processMessage() is final to ensure that mWakeLock is released once 1395 * the Handler queue is empty. Future changes (or overrides) to processMessage() to MUST 1396 * ensure that mWakeLock is correctly released. 1397 */ 1398 @Override processMessage(Message msg)1399 public final boolean processMessage(Message msg) { 1400 final int token = msg.arg1; 1401 if (!isValidToken(token)) { 1402 logDbg("Message called with obsolete token: " + token + "; what: " + msg.what); 1403 return HANDLED; 1404 } 1405 1406 try { 1407 processStateMsg(msg); 1408 } catch (Exception e) { 1409 logWtf("Uncaught exception", e); 1410 sendDisconnectRequestedAndAcquireWakelock( 1411 DISCONNECT_REASON_INTERNAL_ERROR + e.toString(), true /* shouldQuit */); 1412 } 1413 1414 // Attempt to release the WakeLock - only possible if the Handler queue is empty 1415 maybeReleaseWakeLock(); 1416 1417 return HANDLED; 1418 } 1419 processStateMsg(Message msg)1420 protected abstract void processStateMsg(Message msg) throws Exception; 1421 1422 @Override exit()1423 public void exit() { 1424 try { 1425 exitState(); 1426 } catch (Exception e) { 1427 logWtf("Uncaught exception", e); 1428 sendDisconnectRequestedAndAcquireWakelock( 1429 DISCONNECT_REASON_INTERNAL_ERROR + e.toString(), true /* shouldQuit */); 1430 } 1431 } 1432 exitState()1433 protected void exitState() throws Exception {} 1434 logUnhandledMessage(Message msg)1435 protected void logUnhandledMessage(Message msg) { 1436 // Log as unexpected all known messages, and log all else as unknown. 1437 switch (msg.what) { 1438 case EVENT_UNDERLYING_NETWORK_CHANGED: // Fallthrough 1439 case EVENT_RETRY_TIMEOUT_EXPIRED: // Fallthrough 1440 case EVENT_SESSION_LOST: // Fallthrough 1441 case EVENT_SESSION_CLOSED: // Fallthrough 1442 case EVENT_TRANSFORM_CREATED: // Fallthrough 1443 case EVENT_SETUP_COMPLETED: // Fallthrough 1444 case EVENT_DISCONNECT_REQUESTED: // Fallthrough 1445 case EVENT_TEARDOWN_TIMEOUT_EXPIRED: // Fallthrough 1446 case EVENT_SUBSCRIPTIONS_CHANGED: // Fallthrough 1447 case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED: // Fallthrough 1448 case EVENT_MIGRATION_COMPLETED: // Fallthrough 1449 case EVENT_IKE_CONNECTION_INFO_CHANGED: // Fallthrough 1450 case EVENT_DATA_STALL_SUSPECTED: 1451 logUnexpectedEvent(msg.what); 1452 break; 1453 default: 1454 logWtfUnknownEvent(msg.what); 1455 break; 1456 } 1457 } 1458 teardownNetwork()1459 protected void teardownNetwork() { 1460 if (mNetworkAgent != null) { 1461 mNetworkAgent.unregister(); 1462 mNetworkAgent = null; 1463 } 1464 } 1465 handleDisconnectRequested(EventDisconnectRequestedInfo info)1466 protected void handleDisconnectRequested(EventDisconnectRequestedInfo info) { 1467 // TODO(b/180526152): notify VcnStatusCallback for Network loss 1468 1469 logInfo("Tearing down. Cause: " + info.reason + "; quitting = " + info.shouldQuit); 1470 if (info.shouldQuit) { 1471 mIsQuitting.setTrue(); 1472 } 1473 1474 teardownNetwork(); 1475 1476 if (mIkeSession == null) { 1477 // Already disconnected, go straight to DisconnectedState 1478 transitionTo(mDisconnectedState); 1479 } else { 1480 // Still need to wait for full closure 1481 transitionTo(mDisconnectingState); 1482 } 1483 } 1484 handleSafeModeTimeoutExceeded()1485 protected void handleSafeModeTimeoutExceeded() { 1486 mSafeModeTimeoutAlarm = null; 1487 logInfo("Entering safe mode after timeout exceeded"); 1488 1489 // Connectivity for this GatewayConnection is broken; tear down the Network. 1490 teardownNetwork(); 1491 mIsInSafeMode = true; 1492 mGatewayStatusCallback.onSafeModeStatusChanged(); 1493 } 1494 logUnexpectedEvent(int what)1495 protected void logUnexpectedEvent(int what) { 1496 logVdbg( 1497 "Unexpected event code " 1498 + what 1499 + " in state " 1500 + this.getClass().getSimpleName()); 1501 } 1502 logWtfUnknownEvent(int what)1503 protected void logWtfUnknownEvent(int what) { 1504 logWtf("Unknown event code " + what + " in state " + this.getClass().getSimpleName()); 1505 } 1506 } 1507 1508 /** 1509 * State representing the a disconnected VCN tunnel. 1510 * 1511 * <p>This is also is the initial state. 1512 */ 1513 private class DisconnectedState extends BaseState { 1514 @Override enterState()1515 protected void enterState() { 1516 if (mIsQuitting.getValue()) { 1517 quitNow(); // Ignore all queued events; cleanup is complete. 1518 } 1519 1520 if (mIkeSession != null || mNetworkAgent != null) { 1521 logWtf("Active IKE Session or NetworkAgent in DisconnectedState"); 1522 } 1523 1524 cancelSafeModeAlarm(); 1525 } 1526 1527 @Override processStateMsg(Message msg)1528 protected void processStateMsg(Message msg) { 1529 switch (msg.what) { 1530 case EVENT_UNDERLYING_NETWORK_CHANGED: 1531 // First network found; start tunnel 1532 mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying; 1533 1534 if (mUnderlying != null) { 1535 transitionTo(mConnectingState); 1536 } 1537 break; 1538 case EVENT_DISCONNECT_REQUESTED: 1539 if (((EventDisconnectRequestedInfo) msg.obj).shouldQuit) { 1540 mIsQuitting.setTrue(); 1541 1542 quitNow(); 1543 } 1544 break; 1545 default: 1546 logUnhandledMessage(msg); 1547 break; 1548 } 1549 } 1550 1551 @Override exitState()1552 protected void exitState() { 1553 // Safe to blindly set up, as it is cancelled and cleared on entering this state 1554 setSafeModeAlarm(); 1555 } 1556 } 1557 1558 private abstract class ActiveBaseState extends BaseState { 1559 @Override isValidToken(int token)1560 protected boolean isValidToken(int token) { 1561 return (token == TOKEN_ALL || token == mCurrentToken); 1562 } 1563 } 1564 1565 /** 1566 * Transitive state representing a VCN that is tearing down an IKE session. 1567 * 1568 * <p>In this state, the IKE session is in the process of being torn down. If the IKE session 1569 * does not complete teardown in a timely fashion, it will be killed (forcibly closed). 1570 */ 1571 private class DisconnectingState extends ActiveBaseState { 1572 /** 1573 * Whether to skip the RetryTimeoutState and go straight to the ConnectingState. 1574 * 1575 * <p>This is used when an underlying network change triggered a restart on a new network. 1576 * 1577 * <p>Reset (to false) upon exit of the DisconnectingState. 1578 */ 1579 private boolean mSkipRetryTimeout = false; 1580 1581 // TODO(b/178441390): Remove this in favor of resetting retry timers on UND_NET change. setSkipRetryTimeout(boolean shouldSkip)1582 public void setSkipRetryTimeout(boolean shouldSkip) { 1583 mSkipRetryTimeout = shouldSkip; 1584 } 1585 1586 @Override enterState()1587 protected void enterState() throws Exception { 1588 if (mIkeSession == null) { 1589 logWtf("IKE session was already closed when entering Disconnecting state."); 1590 sendMessageAndAcquireWakeLock(EVENT_SESSION_CLOSED, mCurrentToken); 1591 return; 1592 } 1593 1594 // If underlying network has already been lost, save some time and just kill the session 1595 if (mUnderlying == null) { 1596 // Will trigger a EVENT_SESSION_CLOSED as IkeSession shuts down. 1597 mIkeSession.kill(); 1598 return; 1599 } 1600 1601 mIkeSession.close(); 1602 1603 // Safe to blindly set up, as it is cancelled and cleared on exiting this state 1604 setTeardownTimeoutAlarm(); 1605 } 1606 1607 @Override processStateMsg(Message msg)1608 protected void processStateMsg(Message msg) { 1609 switch (msg.what) { 1610 case EVENT_UNDERLYING_NETWORK_CHANGED: // Fallthrough 1611 mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying; 1612 1613 // If we received a new underlying network, continue. 1614 if (mUnderlying != null) { 1615 break; 1616 } 1617 1618 // Fallthrough; no network exists to send IKE close session requests. 1619 case EVENT_TEARDOWN_TIMEOUT_EXPIRED: 1620 // Grace period ended. Kill session, triggering EVENT_SESSION_CLOSED 1621 mIkeSession.kill(); 1622 1623 break; 1624 case EVENT_DISCONNECT_REQUESTED: 1625 EventDisconnectRequestedInfo info = ((EventDisconnectRequestedInfo) msg.obj); 1626 if (info.shouldQuit) { 1627 mIsQuitting.setTrue(); 1628 } 1629 1630 teardownNetwork(); 1631 1632 if (info.reason.equals(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)) { 1633 // TODO(b/180526152): notify VcnStatusCallback for Network loss 1634 1635 // Will trigger EVENT_SESSION_CLOSED immediately. 1636 mIkeSession.kill(); 1637 break; 1638 } 1639 1640 // Otherwise we are already in the process of shutting down. 1641 break; 1642 case EVENT_SESSION_CLOSED: 1643 mIkeSession = null; 1644 1645 if (!mIsQuitting.getValue() && mUnderlying != null) { 1646 transitionTo(mSkipRetryTimeout ? mConnectingState : mRetryTimeoutState); 1647 } else { 1648 teardownNetwork(); 1649 transitionTo(mDisconnectedState); 1650 } 1651 break; 1652 case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED: 1653 handleSafeModeTimeoutExceeded(); 1654 break; 1655 default: 1656 logUnhandledMessage(msg); 1657 break; 1658 } 1659 } 1660 1661 @Override exitState()1662 protected void exitState() throws Exception { 1663 mSkipRetryTimeout = false; 1664 1665 cancelTeardownTimeoutAlarm(); 1666 } 1667 } 1668 1669 /** 1670 * Transitive state representing a VCN that is making an primary (non-handover) connection. 1671 * 1672 * <p>This state starts IKE negotiation, but defers transform application & network setup to the 1673 * Connected state. 1674 */ 1675 private class ConnectingState extends ActiveBaseState { 1676 @Override enterState()1677 protected void enterState() { 1678 if (mIkeSession != null) { 1679 logWtf("ConnectingState entered with active session"); 1680 1681 // Attempt to recover. 1682 mIkeSession.kill(); 1683 mIkeSession = null; 1684 } 1685 1686 mIkeSession = buildIkeSession(mUnderlying.network); 1687 } 1688 1689 @Override processStateMsg(Message msg)1690 protected void processStateMsg(Message msg) { 1691 switch (msg.what) { 1692 case EVENT_UNDERLYING_NETWORK_CHANGED: 1693 final UnderlyingNetworkRecord oldUnderlying = mUnderlying; 1694 mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying; 1695 1696 if (oldUnderlying == null) { 1697 // This should never happen, but if it does, there's likely a nasty bug. 1698 logWtf("Old underlying network was null in connected state. Bug?"); 1699 } 1700 1701 // If new underlying is null, all underlying networks have been lost; disconnect 1702 if (mUnderlying == null) { 1703 transitionTo(mDisconnectingState); 1704 break; 1705 } 1706 1707 if (oldUnderlying != null 1708 && mUnderlying.network.equals(oldUnderlying.network)) { 1709 break; // Only network properties have changed; continue connecting. 1710 } 1711 // Else, retry on the new network. 1712 1713 // Immediately come back to the ConnectingState (skip RetryTimeout, since this 1714 // isn't a failure) 1715 mDisconnectingState.setSkipRetryTimeout(true); 1716 1717 // fallthrough - disconnect, and retry on new network. 1718 case EVENT_SESSION_LOST: 1719 transitionTo(mDisconnectingState); 1720 break; 1721 case EVENT_SESSION_CLOSED: 1722 // Disconnecting state waits for EVENT_SESSION_CLOSED to shutdown, and this 1723 // message may not be posted again. Defer to ensure immediate shutdown. 1724 deferMessage(msg); 1725 1726 transitionTo(mDisconnectingState); 1727 break; 1728 case EVENT_SETUP_COMPLETED: // fallthrough 1729 case EVENT_IKE_CONNECTION_INFO_CHANGED: // fallthrough 1730 case EVENT_TRANSFORM_CREATED: 1731 // Child setup complete; move to ConnectedState for NetworkAgent registration 1732 deferMessage(msg); 1733 transitionTo(mConnectedState); 1734 break; 1735 case EVENT_DISCONNECT_REQUESTED: 1736 handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj); 1737 break; 1738 case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED: 1739 handleSafeModeTimeoutExceeded(); 1740 break; 1741 default: 1742 logUnhandledMessage(msg); 1743 break; 1744 } 1745 } 1746 } 1747 1748 private abstract class ConnectedStateBase extends ActiveBaseState { updateNetworkAgent( @onNull IpSecTunnelInterface tunnelIface, @NonNull VcnNetworkAgent agent, @NonNull VcnChildSessionConfiguration childConfig, @NonNull IkeSessionConnectionInfo ikeConnectionInfo)1749 protected void updateNetworkAgent( 1750 @NonNull IpSecTunnelInterface tunnelIface, 1751 @NonNull VcnNetworkAgent agent, 1752 @NonNull VcnChildSessionConfiguration childConfig, 1753 @NonNull IkeSessionConnectionInfo ikeConnectionInfo) { 1754 final NetworkCapabilities caps = 1755 buildNetworkCapabilities(mConnectionConfig, mUnderlying, mIsMobileDataEnabled); 1756 final LinkProperties lp = 1757 buildConnectedLinkProperties( 1758 mConnectionConfig, 1759 tunnelIface, 1760 childConfig, 1761 mUnderlying, 1762 ikeConnectionInfo); 1763 1764 agent.sendNetworkCapabilities(caps); 1765 agent.sendLinkProperties(lp); 1766 1767 agent.setUnderlyingNetworks( 1768 mUnderlying == null ? null : Collections.singletonList(mUnderlying.network)); 1769 } 1770 buildNetworkAgent( @onNull IpSecTunnelInterface tunnelIface, @NonNull VcnChildSessionConfiguration childConfig, @NonNull IkeSessionConnectionInfo ikeConnectionInfo)1771 protected VcnNetworkAgent buildNetworkAgent( 1772 @NonNull IpSecTunnelInterface tunnelIface, 1773 @NonNull VcnChildSessionConfiguration childConfig, 1774 @NonNull IkeSessionConnectionInfo ikeConnectionInfo) { 1775 final NetworkCapabilities caps = 1776 buildNetworkCapabilities(mConnectionConfig, mUnderlying, mIsMobileDataEnabled); 1777 final LinkProperties lp = 1778 buildConnectedLinkProperties( 1779 mConnectionConfig, 1780 tunnelIface, 1781 childConfig, 1782 mUnderlying, 1783 ikeConnectionInfo); 1784 final NetworkAgentConfig nac = 1785 new NetworkAgentConfig.Builder() 1786 .setLegacyType(ConnectivityManager.TYPE_MOBILE) 1787 .setLegacyTypeName(NETWORK_INFO_NETWORK_TYPE_STRING) 1788 .setLegacySubType(TelephonyManager.NETWORK_TYPE_UNKNOWN) 1789 .setLegacySubTypeName( 1790 TelephonyManager.getNetworkTypeName( 1791 TelephonyManager.NETWORK_TYPE_UNKNOWN)) 1792 .setLegacyExtraInfo(NETWORK_INFO_EXTRA_INFO) 1793 .build(); 1794 1795 final VcnNetworkAgent agent = 1796 mDeps.newNetworkAgent( 1797 mVcnContext, 1798 TAG, 1799 caps, 1800 lp, 1801 Vcn.getNetworkScore(), 1802 nac, 1803 mVcnContext.getVcnNetworkProvider(), 1804 (agentRef) -> { 1805 // Only trigger teardown if the NetworkAgent hasn't been replaced or 1806 // changed. This guards against two cases - the first where 1807 // unwanted() may be called as a result of the 1808 // NetworkAgent.unregister() call, which might trigger a teardown 1809 // instead of just a Network disconnect, as well as the case where a 1810 // new NetworkAgent replaces an old one before the unwanted() call 1811 // is processed. 1812 if (mNetworkAgent != agentRef) { 1813 logDbg("unwanted() called on stale NetworkAgent"); 1814 return; 1815 } 1816 1817 logInfo("NetworkAgent was unwanted"); 1818 teardownAsynchronously(); 1819 } /* networkUnwantedCallback */, 1820 (status) -> { 1821 if (mIsQuitting.getValue()) { 1822 return; // Ignore; VcnGatewayConnection quitting or already quit 1823 } 1824 1825 switch (status) { 1826 case NetworkAgent.VALIDATION_STATUS_VALID: 1827 clearFailedAttemptCounterAndSafeModeAlarm(); 1828 break; 1829 case NetworkAgent.VALIDATION_STATUS_NOT_VALID: 1830 // Trigger re-validation of underlying networks; if it 1831 // fails, the VCN will attempt to migrate away. 1832 if (mUnderlying != null) { 1833 mConnectivityManager.reportNetworkConnectivity( 1834 mUnderlying.network, 1835 false /* hasConnectivity */); 1836 } 1837 1838 // Will only set a new alarm if no safe mode alarm is 1839 // currently scheduled. 1840 setSafeModeAlarm(); 1841 break; 1842 default: 1843 logWtf( 1844 "Unknown validation status " 1845 + status 1846 + "; ignoring"); 1847 break; 1848 } 1849 } /* validationStatusCallback */); 1850 1851 agent.register(); 1852 agent.markConnected(); 1853 1854 return agent; 1855 } 1856 clearFailedAttemptCounterAndSafeModeAlarm()1857 protected void clearFailedAttemptCounterAndSafeModeAlarm() { 1858 mVcnContext.ensureRunningOnLooperThread(); 1859 1860 // Validated connection, clear failed attempt counter 1861 mFailedAttempts = 0; 1862 cancelSafeModeAlarm(); 1863 1864 mIsInSafeMode = false; 1865 mGatewayStatusCallback.onSafeModeStatusChanged(); 1866 } 1867 applyTransform( int token, @NonNull IpSecTunnelInterface tunnelIface, @NonNull Network underlyingNetwork, @NonNull IpSecTransform transform, int direction)1868 protected void applyTransform( 1869 int token, 1870 @NonNull IpSecTunnelInterface tunnelIface, 1871 @NonNull Network underlyingNetwork, 1872 @NonNull IpSecTransform transform, 1873 int direction) { 1874 if (direction != IpSecManager.DIRECTION_IN && direction != IpSecManager.DIRECTION_OUT) { 1875 logWtf("Applying transform for unexpected direction: " + direction); 1876 } 1877 1878 try { 1879 tunnelIface.setUnderlyingNetwork(underlyingNetwork); 1880 1881 // Transforms do not need to be persisted; the IkeSession will keep them alive 1882 mIpSecManager.applyTunnelModeTransform(tunnelIface, direction, transform); 1883 1884 // For inbound transforms, additionally allow forwarded traffic to bridge to DUN (as 1885 // needed) 1886 final Set<Integer> exposedCaps = mConnectionConfig.getAllExposedCapabilities(); 1887 if (direction == IpSecManager.DIRECTION_IN 1888 && exposedCaps.contains(NET_CAPABILITY_DUN)) { 1889 mIpSecManager.applyTunnelModeTransform( 1890 tunnelIface, IpSecManager.DIRECTION_FWD, transform); 1891 } 1892 } catch (IOException e) { 1893 logInfo("Transform application failed for network " + token, e); 1894 sessionLost(token, e); 1895 } 1896 } 1897 setupInterface( int token, @NonNull IpSecTunnelInterface tunnelIface, @NonNull VcnChildSessionConfiguration childConfig, @Nullable VcnChildSessionConfiguration oldChildConfig)1898 protected void setupInterface( 1899 int token, 1900 @NonNull IpSecTunnelInterface tunnelIface, 1901 @NonNull VcnChildSessionConfiguration childConfig, 1902 @Nullable VcnChildSessionConfiguration oldChildConfig) { 1903 try { 1904 final Set<LinkAddress> newAddrs = 1905 new ArraySet<>(childConfig.getInternalAddresses()); 1906 final Set<LinkAddress> existingAddrs = new ArraySet<>(); 1907 if (oldChildConfig != null) { 1908 existingAddrs.addAll(oldChildConfig.getInternalAddresses()); 1909 } 1910 1911 final Set<LinkAddress> toAdd = new ArraySet<>(); 1912 toAdd.addAll(newAddrs); 1913 toAdd.removeAll(existingAddrs); 1914 1915 final Set<LinkAddress> toRemove = new ArraySet<>(); 1916 toRemove.addAll(existingAddrs); 1917 toRemove.removeAll(newAddrs); 1918 1919 for (LinkAddress address : toAdd) { 1920 tunnelIface.addAddress(address.getAddress(), address.getPrefixLength()); 1921 } 1922 1923 for (LinkAddress address : toRemove) { 1924 tunnelIface.removeAddress(address.getAddress(), address.getPrefixLength()); 1925 } 1926 } catch (IOException e) { 1927 logInfo("Adding address to tunnel failed for token " + token, e); 1928 sessionLost(token, e); 1929 } 1930 } 1931 } 1932 1933 /** 1934 * Stable state representing a VCN that has a functioning connection to the mobility anchor. 1935 * 1936 * <p>This state handles IPsec transform application (initial and rekey), NetworkAgent setup, 1937 * and monitors for mobility events. 1938 */ 1939 class ConnectedState extends ConnectedStateBase { 1940 @Override enterState()1941 protected void enterState() throws Exception { 1942 if (mTunnelIface == null) { 1943 try { 1944 // Requires a real Network object in order to be created; doing this any earlier 1945 // means not having a real Network object, or picking an incorrect Network. 1946 mTunnelIface = 1947 mIpSecManager.createIpSecTunnelInterface( 1948 DUMMY_ADDR, DUMMY_ADDR, mUnderlying.network); 1949 } catch (IOException | ResourceUnavailableException e) { 1950 teardownAsynchronously(); 1951 } 1952 } 1953 } 1954 1955 @Override processStateMsg(Message msg)1956 protected void processStateMsg(Message msg) { 1957 switch (msg.what) { 1958 case EVENT_UNDERLYING_NETWORK_CHANGED: 1959 handleUnderlyingNetworkChanged(msg); 1960 break; 1961 case EVENT_SESSION_CLOSED: 1962 // Disconnecting state waits for EVENT_SESSION_CLOSED to shutdown, and this 1963 // message may not be posted again. Defer to ensure immediate shutdown. 1964 deferMessage(msg); 1965 transitionTo(mDisconnectingState); 1966 break; 1967 case EVENT_SESSION_LOST: 1968 transitionTo(mDisconnectingState); 1969 break; 1970 case EVENT_TRANSFORM_CREATED: 1971 final EventTransformCreatedInfo transformCreatedInfo = 1972 (EventTransformCreatedInfo) msg.obj; 1973 1974 applyTransform( 1975 mCurrentToken, 1976 mTunnelIface, 1977 mUnderlying.network, 1978 transformCreatedInfo.transform, 1979 transformCreatedInfo.direction); 1980 break; 1981 case EVENT_SETUP_COMPLETED: 1982 final VcnChildSessionConfiguration oldChildConfig = mChildConfig; 1983 mChildConfig = ((EventSetupCompletedInfo) msg.obj).childSessionConfig; 1984 1985 setupInterfaceAndNetworkAgent( 1986 mCurrentToken, 1987 mTunnelIface, 1988 mChildConfig, 1989 oldChildConfig, 1990 mIkeConnectionInfo); 1991 1992 // Create opportunistic child SAs; this allows SA aggregation in the downlink, 1993 // reducing lock/atomic contention in high throughput scenarios. All SAs will 1994 // share the same UDP encap socket (and keepalives) as necessary, and are 1995 // effectively free. 1996 final int parallelTunnelCount = 1997 mDeps.getParallelTunnelCount(mLastSnapshot, mSubscriptionGroup); 1998 logInfo("Parallel tunnel count: " + parallelTunnelCount); 1999 2000 for (int i = 0; i < parallelTunnelCount - 1; i++) { 2001 mIkeSession.openChildSession( 2002 buildOpportunisticChildParams(), 2003 new VcnChildSessionCallback( 2004 mCurrentToken, true /* isOpportunistic */)); 2005 } 2006 2007 break; 2008 case EVENT_DISCONNECT_REQUESTED: 2009 handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj); 2010 break; 2011 case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED: 2012 handleSafeModeTimeoutExceeded(); 2013 break; 2014 case EVENT_MIGRATION_COMPLETED: 2015 final EventMigrationCompletedInfo migrationCompletedInfo = 2016 (EventMigrationCompletedInfo) msg.obj; 2017 2018 handleMigrationCompleted(migrationCompletedInfo); 2019 break; 2020 case EVENT_IKE_CONNECTION_INFO_CHANGED: 2021 mIkeConnectionInfo = 2022 ((EventIkeConnectionInfoChangedInfo) msg.obj).ikeConnectionInfo; 2023 break; 2024 case EVENT_DATA_STALL_SUSPECTED: 2025 final Network networkWithDataStall = 2026 ((EventDataStallSuspectedInfo) msg.obj).network; 2027 handleDataStallSuspected(networkWithDataStall); 2028 break; 2029 default: 2030 logUnhandledMessage(msg); 2031 break; 2032 } 2033 } 2034 handleMigrationCompleted(EventMigrationCompletedInfo migrationCompletedInfo)2035 private void handleMigrationCompleted(EventMigrationCompletedInfo migrationCompletedInfo) { 2036 logInfo("Migration completed: " + mUnderlying.network); 2037 2038 applyTransform( 2039 mCurrentToken, 2040 mTunnelIface, 2041 mUnderlying.network, 2042 migrationCompletedInfo.inTransform, 2043 IpSecManager.DIRECTION_IN); 2044 2045 applyTransform( 2046 mCurrentToken, 2047 mTunnelIface, 2048 mUnderlying.network, 2049 migrationCompletedInfo.outTransform, 2050 IpSecManager.DIRECTION_OUT); 2051 2052 updateNetworkAgent(mTunnelIface, mNetworkAgent, mChildConfig, mIkeConnectionInfo); 2053 2054 // Trigger re-validation after migration events. 2055 mConnectivityManager.reportNetworkConnectivity( 2056 mNetworkAgent.getNetwork(), false /* hasConnectivity */); 2057 } 2058 handleUnderlyingNetworkChanged(@onNull Message msg)2059 private void handleUnderlyingNetworkChanged(@NonNull Message msg) { 2060 final UnderlyingNetworkRecord oldUnderlying = mUnderlying; 2061 mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying; 2062 2063 if (mUnderlying == null) { 2064 logInfo("Underlying network lost"); 2065 2066 // Ignored for now; a new network may be coming up. If none does, the delayed 2067 // NETWORK_LOST disconnect will be fired, and tear down the session + network. 2068 return; 2069 } 2070 2071 // mUnderlying assumed non-null, given check above. 2072 // If network changed, migrate. Otherwise, update any existing networkAgent. 2073 if (oldUnderlying == null || !oldUnderlying.network.equals(mUnderlying.network)) { 2074 logInfo("Migrating to new network: " + mUnderlying.network); 2075 mIkeSession.setNetwork(mUnderlying.network); 2076 } else { 2077 // oldUnderlying is non-null & underlying network itself has not changed 2078 // (only network properties were changed). 2079 2080 // Network not yet set up, or child not yet connected. 2081 if (mNetworkAgent != null && mChildConfig != null) { 2082 // If only network properties changed and agent is active, update properties 2083 updateNetworkAgent( 2084 mTunnelIface, mNetworkAgent, mChildConfig, mIkeConnectionInfo); 2085 } 2086 } 2087 } 2088 handleDataStallSuspected(Network networkWithDataStall)2089 private void handleDataStallSuspected(Network networkWithDataStall) { 2090 if (mUnderlying != null 2091 && mNetworkAgent != null 2092 && mNetworkAgent.getNetwork().equals(networkWithDataStall)) { 2093 logInfo("Perform Mobility update to recover from suspected data stall"); 2094 mIkeSession.setNetwork(mUnderlying.network); 2095 } 2096 } 2097 setupInterfaceAndNetworkAgent( int token, @NonNull IpSecTunnelInterface tunnelIface, @NonNull VcnChildSessionConfiguration childConfig, @NonNull VcnChildSessionConfiguration oldChildConfig, @NonNull IkeSessionConnectionInfo ikeConnectionInfo)2098 protected void setupInterfaceAndNetworkAgent( 2099 int token, 2100 @NonNull IpSecTunnelInterface tunnelIface, 2101 @NonNull VcnChildSessionConfiguration childConfig, 2102 @NonNull VcnChildSessionConfiguration oldChildConfig, 2103 @NonNull IkeSessionConnectionInfo ikeConnectionInfo) { 2104 setupInterface(token, tunnelIface, childConfig, oldChildConfig); 2105 2106 if (mNetworkAgent == null) { 2107 mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig, ikeConnectionInfo); 2108 } else { 2109 updateNetworkAgent(tunnelIface, mNetworkAgent, childConfig, ikeConnectionInfo); 2110 2111 // mNetworkAgent not null, so the VCN Network has already been established. Clear 2112 // the failed attempt counter and safe mode alarm since this transition is complete. 2113 clearFailedAttemptCounterAndSafeModeAlarm(); 2114 } 2115 } 2116 2117 @Override exitState()2118 protected void exitState() { 2119 // Will only set a new alarm if no safe mode alarm is currently scheduled. 2120 setSafeModeAlarm(); 2121 } 2122 } 2123 2124 /** 2125 * Transitive state representing a VCN that failed to establish a connection, and will retry. 2126 * 2127 * <p>This state will be exited upon a new underlying network being found, or timeout expiry. 2128 */ 2129 class RetryTimeoutState extends ActiveBaseState { 2130 @Override enterState()2131 protected void enterState() throws Exception { 2132 // Reset upon entry to ConnectedState 2133 mFailedAttempts++; 2134 2135 if (mUnderlying == null) { 2136 logWtf("Underlying network was null in retry state"); 2137 teardownNetwork(); 2138 transitionTo(mDisconnectedState); 2139 } else { 2140 // Safe to blindly set up, as it is cancelled and cleared on exiting this state 2141 setRetryTimeoutAlarm(getNextRetryIntervalsMs()); 2142 } 2143 } 2144 2145 @Override processStateMsg(Message msg)2146 protected void processStateMsg(Message msg) { 2147 switch (msg.what) { 2148 case EVENT_UNDERLYING_NETWORK_CHANGED: 2149 final UnderlyingNetworkRecord oldUnderlying = mUnderlying; 2150 mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying; 2151 2152 // If new underlying is null, all networks were lost; go back to disconnected. 2153 if (mUnderlying == null) { 2154 teardownNetwork(); 2155 transitionTo(mDisconnectedState); 2156 return; 2157 } else if (oldUnderlying != null 2158 && mUnderlying.network.equals(oldUnderlying.network)) { 2159 // If the network has not changed, do nothing. 2160 return; 2161 } 2162 2163 // Fallthrough 2164 case EVENT_RETRY_TIMEOUT_EXPIRED: 2165 transitionTo(mConnectingState); 2166 break; 2167 case EVENT_DISCONNECT_REQUESTED: 2168 handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj); 2169 break; 2170 case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED: 2171 handleSafeModeTimeoutExceeded(); 2172 break; 2173 default: 2174 logUnhandledMessage(msg); 2175 break; 2176 } 2177 } 2178 2179 @Override exitState()2180 public void exitState() { 2181 cancelRetryTimeoutAlarm(); 2182 } 2183 getNextRetryIntervalsMs()2184 private long getNextRetryIntervalsMs() { 2185 final int retryDelayIndex = mFailedAttempts - 1; 2186 final long[] retryIntervalsMs = mConnectionConfig.getRetryIntervalsMillis(); 2187 2188 // Repeatedly use last item in retry timeout list. 2189 if (retryDelayIndex >= retryIntervalsMs.length) { 2190 return retryIntervalsMs[retryIntervalsMs.length - 1]; 2191 } 2192 2193 return retryIntervalsMs[retryDelayIndex]; 2194 } 2195 } 2196 2197 @VisibleForTesting(visibility = Visibility.PRIVATE) buildNetworkCapabilities( @onNull VcnGatewayConnectionConfig gatewayConnectionConfig, @Nullable UnderlyingNetworkRecord underlying, boolean isMobileDataEnabled)2198 static NetworkCapabilities buildNetworkCapabilities( 2199 @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig, 2200 @Nullable UnderlyingNetworkRecord underlying, 2201 boolean isMobileDataEnabled) { 2202 final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(); 2203 2204 builder.addTransportType(TRANSPORT_CELLULAR); 2205 builder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); 2206 builder.addCapability(NET_CAPABILITY_NOT_CONGESTED); 2207 builder.addCapability(NET_CAPABILITY_NOT_SUSPENDED); 2208 2209 // Add exposed capabilities 2210 for (int cap : gatewayConnectionConfig.getAllExposedCapabilities()) { 2211 // Skip adding INTERNET or DUN if mobile data is disabled. 2212 if (!isMobileDataEnabled 2213 && (cap == NET_CAPABILITY_INTERNET || cap == NET_CAPABILITY_DUN)) { 2214 continue; 2215 } 2216 2217 builder.addCapability(cap); 2218 } 2219 2220 if (underlying != null) { 2221 final NetworkCapabilities underlyingCaps = underlying.networkCapabilities; 2222 2223 // Mirror merged capabilities. 2224 for (int cap : MERGED_CAPABILITIES) { 2225 if (underlyingCaps.hasCapability(cap)) { 2226 builder.addCapability(cap); 2227 } 2228 } 2229 2230 // Set admin UIDs for ConnectivityDiagnostics use. 2231 final int[] underlyingAdminUids = underlyingCaps.getAdministratorUids(); 2232 Arrays.sort(underlyingAdminUids); // Sort to allow contains check below. 2233 2234 int[] adminUids; 2235 if (underlyingCaps.getOwnerUid() > 0 // No owner UID specified 2236 && 0 > Arrays.binarySearch(// Owner UID not found in admin UID list. 2237 underlyingAdminUids, underlyingCaps.getOwnerUid())) { 2238 adminUids = Arrays.copyOf(underlyingAdminUids, underlyingAdminUids.length + 1); 2239 adminUids[adminUids.length - 1] = underlyingCaps.getOwnerUid(); 2240 Arrays.sort(adminUids); 2241 } else { 2242 adminUids = underlyingAdminUids; 2243 } 2244 2245 // Set owner & administrator UID 2246 builder.setOwnerUid(Process.myUid()); 2247 adminUids = Arrays.copyOf(adminUids, adminUids.length + 1); 2248 adminUids[adminUids.length - 1] = Process.myUid(); 2249 builder.setAdministratorUids(adminUids); 2250 2251 builder.setLinkUpstreamBandwidthKbps(underlyingCaps.getLinkUpstreamBandwidthKbps()); 2252 builder.setLinkDownstreamBandwidthKbps(underlyingCaps.getLinkDownstreamBandwidthKbps()); 2253 2254 // Set TransportInfo for SysUI use (never parcelled out of SystemServer). 2255 if (underlyingCaps.hasTransport(TRANSPORT_WIFI) 2256 && underlyingCaps.getTransportInfo() instanceof WifiInfo) { 2257 final WifiInfo wifiInfo = (WifiInfo) underlyingCaps.getTransportInfo(); 2258 builder.setTransportInfo( 2259 new VcnTransportInfo( 2260 wifiInfo, 2261 gatewayConnectionConfig.getMinUdpPort4500NatTimeoutSeconds())); 2262 } else if (underlyingCaps.hasTransport(TRANSPORT_CELLULAR) 2263 && underlyingCaps.getNetworkSpecifier() instanceof TelephonyNetworkSpecifier) { 2264 final TelephonyNetworkSpecifier telNetSpecifier = 2265 (TelephonyNetworkSpecifier) underlyingCaps.getNetworkSpecifier(); 2266 builder.setTransportInfo( 2267 new VcnTransportInfo( 2268 telNetSpecifier.getSubscriptionId(), 2269 gatewayConnectionConfig.getMinUdpPort4500NatTimeoutSeconds())); 2270 } else { 2271 Slog.wtf( 2272 TAG, 2273 "Unknown transport type or missing TransportInfo/NetworkSpecifier for" 2274 + " non-null underlying network"); 2275 } 2276 builder.setUnderlyingNetworks(List.of(underlying.network)); 2277 } else { 2278 Slog.wtf( 2279 TAG, 2280 "No underlying network while building network capabilities", 2281 new IllegalStateException()); 2282 } 2283 2284 return builder.build(); 2285 } 2286 2287 @VisibleForTesting(visibility = Visibility.PRIVATE) buildConnectedLinkProperties( @onNull VcnGatewayConnectionConfig gatewayConnectionConfig, @NonNull IpSecTunnelInterface tunnelIface, @NonNull VcnChildSessionConfiguration childConfig, @Nullable UnderlyingNetworkRecord underlying, @NonNull IkeSessionConnectionInfo ikeConnectionInfo)2288 LinkProperties buildConnectedLinkProperties( 2289 @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig, 2290 @NonNull IpSecTunnelInterface tunnelIface, 2291 @NonNull VcnChildSessionConfiguration childConfig, 2292 @Nullable UnderlyingNetworkRecord underlying, 2293 @NonNull IkeSessionConnectionInfo ikeConnectionInfo) { 2294 final IkeTunnelConnectionParams ikeTunnelParams = 2295 gatewayConnectionConfig.getTunnelConnectionParams(); 2296 final LinkProperties lp = new LinkProperties(); 2297 2298 lp.setInterfaceName(tunnelIface.getInterfaceName()); 2299 for (LinkAddress addr : childConfig.getInternalAddresses()) { 2300 lp.addLinkAddress(addr); 2301 } 2302 for (InetAddress addr : childConfig.getInternalDnsServers()) { 2303 lp.addDnsServer(addr); 2304 } 2305 2306 lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null /*gateway*/, 2307 null /*iface*/, RouteInfo.RTN_UNICAST)); 2308 lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null /*gateway*/, 2309 null /*iface*/, RouteInfo.RTN_UNICAST)); 2310 2311 int underlyingMtu = 0; 2312 if (underlying != null) { 2313 final LinkProperties underlyingLp = underlying.linkProperties; 2314 2315 lp.setTcpBufferSizes(underlyingLp.getTcpBufferSizes()); 2316 underlyingMtu = underlyingLp.getMtu(); 2317 2318 // WiFi LinkProperties uses DHCP as the sole source of MTU information, and as a result 2319 // often lists MTU as 0 (see b/184678973). Use the interface MTU as retrieved by 2320 // NetworkInterface APIs. 2321 if (underlyingMtu == 0 && underlyingLp.getInterfaceName() != null) { 2322 underlyingMtu = mDeps.getUnderlyingIfaceMtu(underlyingLp.getInterfaceName()); 2323 } 2324 } else { 2325 Slog.wtf( 2326 TAG, 2327 "No underlying network while building link properties", 2328 new IllegalStateException()); 2329 } 2330 lp.setMtu( 2331 MtuUtils.getMtu( 2332 ikeTunnelParams.getTunnelModeChildSessionParams().getSaProposals(), 2333 gatewayConnectionConfig.getMaxMtu(), 2334 underlyingMtu, 2335 ikeConnectionInfo.getLocalAddress() instanceof Inet4Address)); 2336 2337 return lp; 2338 } 2339 2340 private class IkeSessionCallbackImpl implements IkeSessionCallback { 2341 private final int mToken; 2342 IkeSessionCallbackImpl(int token)2343 IkeSessionCallbackImpl(int token) { 2344 mToken = token; 2345 } 2346 2347 @Override onOpened(@onNull IkeSessionConfiguration ikeSessionConfig)2348 public void onOpened(@NonNull IkeSessionConfiguration ikeSessionConfig) { 2349 logDbg("IkeOpened for token " + mToken); 2350 ikeConnectionInfoChanged(mToken, ikeSessionConfig.getIkeSessionConnectionInfo()); 2351 } 2352 2353 @Override onClosed()2354 public void onClosed() { 2355 logDbg("IkeClosed for token " + mToken); 2356 sessionClosed(mToken, null); 2357 } 2358 2359 @Override onClosedExceptionally(@onNull IkeException exception)2360 public void onClosedExceptionally(@NonNull IkeException exception) { 2361 logInfo("IkeClosedExceptionally for token " + mToken, exception); 2362 sessionClosed(mToken, exception); 2363 } 2364 2365 @Override onError(@onNull IkeProtocolException exception)2366 public void onError(@NonNull IkeProtocolException exception) { 2367 logInfo("IkeError for token " + mToken, exception); 2368 // Non-fatal, log and continue. 2369 } 2370 2371 @Override onIkeSessionConnectionInfoChanged( @onNull IkeSessionConnectionInfo connectionInfo)2372 public void onIkeSessionConnectionInfoChanged( 2373 @NonNull IkeSessionConnectionInfo connectionInfo) { 2374 logDbg("onIkeSessionConnectionInfoChanged for token " + mToken); 2375 ikeConnectionInfoChanged(mToken, connectionInfo); 2376 } 2377 } 2378 2379 /** Implementation of ChildSessionCallback, exposed for testing. */ 2380 @VisibleForTesting(visibility = Visibility.PRIVATE) 2381 public class VcnChildSessionCallback implements ChildSessionCallback { 2382 private final int mToken; 2383 private final boolean mIsOpportunistic; 2384 2385 private boolean mIsChildOpened = false; 2386 VcnChildSessionCallback(int token)2387 VcnChildSessionCallback(int token) { 2388 this(token, false /* isOpportunistic */); 2389 } 2390 2391 /** 2392 * Creates a ChildSessionCallback 2393 * 2394 * <p>If configured as opportunistic, transforms will not report initial startup, or 2395 * associated startup failures. This serves the dual purposes of ensuring that if the server 2396 * does not support connection multiplexing, new child SA negotiations will be ignored, and 2397 * at the same time, will notify the VCN session if a successfully negotiated opportunistic 2398 * child SA is subsequently torn down, which could impact uplink traffic if the SA in use 2399 * for outbound/uplink traffic is this opportunistic SA. 2400 * 2401 * <p>While inbound SAs can be used in parallel, the IPsec stack explicitly selects the last 2402 * applied outbound transform for outbound traffic. This means that unlike inbound traffic, 2403 * outbound does not benefit from these parallel SAs in the same manner. 2404 */ VcnChildSessionCallback(int token, boolean isOpportunistic)2405 VcnChildSessionCallback(int token, boolean isOpportunistic) { 2406 mToken = token; 2407 mIsOpportunistic = isOpportunistic; 2408 } 2409 2410 /** Internal proxy method for injecting of mocked ChildSessionConfiguration */ 2411 @VisibleForTesting(visibility = Visibility.PRIVATE) onOpened(@onNull VcnChildSessionConfiguration childConfig)2412 void onOpened(@NonNull VcnChildSessionConfiguration childConfig) { 2413 logDbg("ChildOpened for token " + mToken); 2414 2415 if (mIsOpportunistic) { 2416 logDbg("ChildOpened for opportunistic child; suppressing event message"); 2417 mIsChildOpened = true; 2418 return; 2419 } 2420 2421 childOpened(mToken, childConfig); 2422 } 2423 2424 @Override onOpened(@onNull ChildSessionConfiguration childConfig)2425 public void onOpened(@NonNull ChildSessionConfiguration childConfig) { 2426 onOpened(new VcnChildSessionConfiguration(childConfig)); 2427 } 2428 2429 @Override onClosed()2430 public void onClosed() { 2431 logDbg("ChildClosed for token " + mToken); 2432 2433 if (mIsOpportunistic && !mIsChildOpened) { 2434 logDbg("ChildClosed for unopened opportunistic child; ignoring"); 2435 return; 2436 } 2437 2438 sessionLost(mToken, null); 2439 } 2440 2441 @Override onClosedExceptionally(@onNull IkeException exception)2442 public void onClosedExceptionally(@NonNull IkeException exception) { 2443 logInfo("ChildClosedExceptionally for token " + mToken, exception); 2444 2445 if (mIsOpportunistic && !mIsChildOpened) { 2446 logInfo("ChildClosedExceptionally for unopened opportunistic child; ignoring"); 2447 return; 2448 } 2449 2450 sessionLost(mToken, exception); 2451 } 2452 2453 @Override onIpSecTransformCreated(@onNull IpSecTransform transform, int direction)2454 public void onIpSecTransformCreated(@NonNull IpSecTransform transform, int direction) { 2455 logDbg("ChildTransformCreated; Direction: " + direction + "; token " + mToken); 2456 childTransformCreated(mToken, transform, direction); 2457 } 2458 2459 @Override onIpSecTransformsMigrated( @onNull IpSecTransform inIpSecTransform, @NonNull IpSecTransform outIpSecTransform)2460 public void onIpSecTransformsMigrated( 2461 @NonNull IpSecTransform inIpSecTransform, 2462 @NonNull IpSecTransform outIpSecTransform) { 2463 logDbg("ChildTransformsMigrated; token " + mToken); 2464 migrationCompleted(mToken, inIpSecTransform, outIpSecTransform); 2465 } 2466 2467 @Override onIpSecTransformDeleted(@onNull IpSecTransform transform, int direction)2468 public void onIpSecTransformDeleted(@NonNull IpSecTransform transform, int direction) { 2469 // Nothing to be done; no references to the IpSecTransform are held, and this transform 2470 // will be closed by the IKE library. 2471 logDbg("ChildTransformDeleted; Direction: " + direction + "; for token " + mToken); 2472 } 2473 } 2474 2475 // Used in Vcn.java, but must be public for mockito to mock this. getLogPrefix()2476 public String getLogPrefix() { 2477 return "(" 2478 + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup) 2479 + "-" 2480 + mConnectionConfig.getGatewayConnectionName() 2481 + "-" 2482 + System.identityHashCode(this) 2483 + ") "; 2484 } 2485 getTagLogPrefix()2486 private String getTagLogPrefix() { 2487 return "[ " + TAG + " " + getLogPrefix() + "]"; 2488 } 2489 logVdbg(String msg)2490 private void logVdbg(String msg) { 2491 if (VDBG) { 2492 Slog.v(TAG, getLogPrefix() + msg); 2493 } 2494 } 2495 logDbg(String msg)2496 private void logDbg(String msg) { 2497 Slog.d(TAG, getLogPrefix() + msg); 2498 } 2499 logDbg(String msg, Throwable tr)2500 private void logDbg(String msg, Throwable tr) { 2501 Slog.d(TAG, getLogPrefix() + msg, tr); 2502 } 2503 logInfo(String msg)2504 private void logInfo(String msg) { 2505 Slog.i(TAG, getLogPrefix() + msg); 2506 LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg); 2507 } 2508 logInfo(String msg, Throwable tr)2509 private void logInfo(String msg, Throwable tr) { 2510 Slog.i(TAG, getLogPrefix() + msg, tr); 2511 LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg + tr); 2512 } 2513 logWarn(String msg)2514 private void logWarn(String msg) { 2515 Slog.w(TAG, getLogPrefix() + msg); 2516 LOCAL_LOG.log("[WARN] " + getTagLogPrefix() + msg); 2517 } 2518 logWarn(String msg, Throwable tr)2519 private void logWarn(String msg, Throwable tr) { 2520 Slog.w(TAG, getLogPrefix() + msg, tr); 2521 LOCAL_LOG.log("[WARN] " + getTagLogPrefix() + msg + tr); 2522 } 2523 logErr(String msg)2524 private void logErr(String msg) { 2525 Slog.e(TAG, getLogPrefix() + msg); 2526 LOCAL_LOG.log("[ERR ] " + getTagLogPrefix() + msg); 2527 } 2528 logErr(String msg, Throwable tr)2529 private void logErr(String msg, Throwable tr) { 2530 Slog.e(TAG, getLogPrefix() + msg, tr); 2531 LOCAL_LOG.log("[ERR ] " + getTagLogPrefix() + msg + tr); 2532 } 2533 logWtf(String msg)2534 private void logWtf(String msg) { 2535 Slog.wtf(TAG, getLogPrefix() + msg); 2536 LOCAL_LOG.log("[WTF ] " + msg); 2537 } 2538 logWtf(String msg, Throwable tr)2539 private void logWtf(String msg, Throwable tr) { 2540 Slog.wtf(TAG, getLogPrefix() + msg, tr); 2541 LOCAL_LOG.log("[WTF ] " + msg + tr); 2542 } 2543 2544 /** 2545 * Dumps the state of this VcnGatewayConnection for logging and debugging purposes. 2546 * 2547 * <p>PII and credentials MUST NEVER be dumped here. 2548 */ dump(IndentingPrintWriter pw)2549 public void dump(IndentingPrintWriter pw) { 2550 pw.println("VcnGatewayConnection (" + mConnectionConfig.getGatewayConnectionName() + "):"); 2551 pw.increaseIndent(); 2552 2553 pw.println( 2554 "Current state: " 2555 + (getCurrentState() == null 2556 ? null 2557 : getCurrentState().getClass().getSimpleName())); 2558 pw.println("mIsQuitting: " + mIsQuitting.getValue()); 2559 pw.println("mIsInSafeMode: " + mIsInSafeMode); 2560 pw.println("mCurrentToken: " + mCurrentToken); 2561 pw.println("mFailedAttempts: " + mFailedAttempts); 2562 pw.println( 2563 "mNetworkAgent.getNetwork(): " 2564 + (mNetworkAgent == null ? null : mNetworkAgent.getNetwork())); 2565 pw.println(); 2566 2567 mUnderlyingNetworkController.dump(pw); 2568 pw.println(); 2569 2570 pw.decreaseIndent(); 2571 } 2572 2573 @VisibleForTesting(visibility = Visibility.PRIVATE) setTunnelInterface(IpSecTunnelInterface tunnelIface)2574 void setTunnelInterface(IpSecTunnelInterface tunnelIface) { 2575 mTunnelIface = tunnelIface; 2576 } 2577 2578 @VisibleForTesting(visibility = Visibility.PRIVATE) getUnderlyingNetworkControllerCallback()2579 UnderlyingNetworkControllerCallback getUnderlyingNetworkControllerCallback() { 2580 return mUnderlyingNetworkControllerCallback; 2581 } 2582 2583 @VisibleForTesting(visibility = Visibility.PRIVATE) getConnectivityDiagnosticsCallback()2584 ConnectivityDiagnosticsCallback getConnectivityDiagnosticsCallback() { 2585 return mConnectivityDiagnosticsCallback; 2586 } 2587 2588 @VisibleForTesting(visibility = Visibility.PRIVATE) getUnderlyingNetwork()2589 UnderlyingNetworkRecord getUnderlyingNetwork() { 2590 return mUnderlying; 2591 } 2592 2593 @VisibleForTesting(visibility = Visibility.PRIVATE) setUnderlyingNetwork(@ullable UnderlyingNetworkRecord record)2594 void setUnderlyingNetwork(@Nullable UnderlyingNetworkRecord record) { 2595 mUnderlying = record; 2596 } 2597 2598 @VisibleForTesting(visibility = Visibility.PRIVATE) getIkeConnectionInfo()2599 IkeSessionConnectionInfo getIkeConnectionInfo() { 2600 return mIkeConnectionInfo; 2601 } 2602 2603 @VisibleForTesting(visibility = Visibility.PRIVATE) isQuitting()2604 boolean isQuitting() { 2605 return mIsQuitting.getValue(); 2606 } 2607 2608 @VisibleForTesting(visibility = Visibility.PRIVATE) setQuitting()2609 void setQuitting() { 2610 mIsQuitting.setTrue(); 2611 } 2612 2613 @VisibleForTesting(visibility = Visibility.PRIVATE) getIkeSession()2614 VcnIkeSession getIkeSession() { 2615 return mIkeSession; 2616 } 2617 2618 @VisibleForTesting(visibility = Visibility.PRIVATE) setIkeSession(@ullable VcnIkeSession session)2619 void setIkeSession(@Nullable VcnIkeSession session) { 2620 mIkeSession = session; 2621 } 2622 2623 @VisibleForTesting(visibility = Visibility.PRIVATE) getNetworkAgent()2624 VcnNetworkAgent getNetworkAgent() { 2625 return mNetworkAgent; 2626 } 2627 2628 @VisibleForTesting(visibility = Visibility.PRIVATE) setNetworkAgent(@ullable VcnNetworkAgent networkAgent)2629 void setNetworkAgent(@Nullable VcnNetworkAgent networkAgent) { 2630 mNetworkAgent = networkAgent; 2631 } 2632 2633 @VisibleForTesting(visibility = Visibility.PRIVATE) sendDisconnectRequestedAndAcquireWakelock(String reason, boolean shouldQuit)2634 void sendDisconnectRequestedAndAcquireWakelock(String reason, boolean shouldQuit) { 2635 sendMessageAndAcquireWakeLock( 2636 EVENT_DISCONNECT_REQUESTED, 2637 TOKEN_ALL, 2638 new EventDisconnectRequestedInfo(reason, shouldQuit)); 2639 } 2640 buildIkeParams(@onNull Network network)2641 private IkeSessionParams buildIkeParams(@NonNull Network network) { 2642 final IkeTunnelConnectionParams ikeTunnelConnectionParams = 2643 mConnectionConfig.getTunnelConnectionParams(); 2644 final IkeSessionParams.Builder builder = 2645 new IkeSessionParams.Builder(ikeTunnelConnectionParams.getIkeSessionParams()); 2646 builder.setNetwork(network); 2647 return builder.build(); 2648 } 2649 buildChildParams()2650 private ChildSessionParams buildChildParams() { 2651 return mConnectionConfig.getTunnelConnectionParams().getTunnelModeChildSessionParams(); 2652 } 2653 buildOpportunisticChildParams()2654 private ChildSessionParams buildOpportunisticChildParams() { 2655 final ChildSessionParams baseParams = 2656 mConnectionConfig.getTunnelConnectionParams().getTunnelModeChildSessionParams(); 2657 2658 final TunnelModeChildSessionParams.Builder builder = 2659 new TunnelModeChildSessionParams.Builder(); 2660 for (ChildSaProposal proposal : baseParams.getChildSaProposals()) { 2661 builder.addChildSaProposal(proposal); 2662 } 2663 2664 for (IkeTrafficSelector inboundSelector : baseParams.getInboundTrafficSelectors()) { 2665 builder.addInboundTrafficSelectors(inboundSelector); 2666 } 2667 2668 for (IkeTrafficSelector outboundSelector : baseParams.getOutboundTrafficSelectors()) { 2669 builder.addOutboundTrafficSelectors(outboundSelector); 2670 } 2671 2672 builder.setLifetimeSeconds( 2673 baseParams.getHardLifetimeSeconds(), baseParams.getSoftLifetimeSeconds()); 2674 2675 return builder.build(); 2676 } 2677 2678 @VisibleForTesting(visibility = Visibility.PRIVATE) buildIkeSession(@onNull Network network)2679 VcnIkeSession buildIkeSession(@NonNull Network network) { 2680 final int token = ++mCurrentToken; 2681 2682 return mDeps.newIkeSession( 2683 mVcnContext, 2684 buildIkeParams(network), 2685 buildChildParams(), 2686 new IkeSessionCallbackImpl(token), 2687 new VcnChildSessionCallback(token)); 2688 } 2689 2690 /** External dependencies used by VcnGatewayConnection, for injection in tests */ 2691 @VisibleForTesting(visibility = Visibility.PRIVATE) 2692 public static class Dependencies { 2693 /** Builds a new UnderlyingNetworkController. */ newUnderlyingNetworkController( VcnContext vcnContext, VcnGatewayConnectionConfig connectionConfig, ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, UnderlyingNetworkControllerCallback callback)2694 public UnderlyingNetworkController newUnderlyingNetworkController( 2695 VcnContext vcnContext, 2696 VcnGatewayConnectionConfig connectionConfig, 2697 ParcelUuid subscriptionGroup, 2698 TelephonySubscriptionSnapshot snapshot, 2699 UnderlyingNetworkControllerCallback callback) { 2700 return new UnderlyingNetworkController( 2701 vcnContext, connectionConfig, subscriptionGroup, snapshot, callback); 2702 } 2703 2704 /** Builds a new IkeSession. */ newIkeSession( VcnContext vcnContext, IkeSessionParams ikeSessionParams, ChildSessionParams childSessionParams, IkeSessionCallback ikeSessionCallback, ChildSessionCallback childSessionCallback)2705 public VcnIkeSession newIkeSession( 2706 VcnContext vcnContext, 2707 IkeSessionParams ikeSessionParams, 2708 ChildSessionParams childSessionParams, 2709 IkeSessionCallback ikeSessionCallback, 2710 ChildSessionCallback childSessionCallback) { 2711 return new VcnIkeSession( 2712 vcnContext, 2713 ikeSessionParams, 2714 childSessionParams, 2715 ikeSessionCallback, 2716 childSessionCallback); 2717 } 2718 2719 /** Builds a new WakeLock. */ newWakeLock( @onNull Context context, int wakeLockFlag, @NonNull String wakeLockTag)2720 public VcnWakeLock newWakeLock( 2721 @NonNull Context context, int wakeLockFlag, @NonNull String wakeLockTag) { 2722 return new VcnWakeLock(context, wakeLockFlag, wakeLockTag); 2723 } 2724 2725 /** Builds a new WakeupMessage. */ newWakeupMessage( @onNull VcnContext vcnContext, @NonNull Handler handler, @NonNull String tag, @NonNull Runnable runnable)2726 public WakeupMessage newWakeupMessage( 2727 @NonNull VcnContext vcnContext, 2728 @NonNull Handler handler, 2729 @NonNull String tag, 2730 @NonNull Runnable runnable) { 2731 return new WakeupMessage(vcnContext.getContext(), handler, tag, runnable); 2732 } 2733 2734 /** Builds a new VcnNetworkAgent. */ newNetworkAgent( @onNull VcnContext vcnContext, @NonNull String tag, @NonNull NetworkCapabilities caps, @NonNull LinkProperties lp, @NonNull NetworkScore score, @NonNull NetworkAgentConfig nac, @NonNull NetworkProvider provider, @NonNull Consumer<VcnNetworkAgent> networkUnwantedCallback, @NonNull Consumer<Integer> validationStatusCallback)2735 public VcnNetworkAgent newNetworkAgent( 2736 @NonNull VcnContext vcnContext, 2737 @NonNull String tag, 2738 @NonNull NetworkCapabilities caps, 2739 @NonNull LinkProperties lp, 2740 @NonNull NetworkScore score, 2741 @NonNull NetworkAgentConfig nac, 2742 @NonNull NetworkProvider provider, 2743 @NonNull Consumer<VcnNetworkAgent> networkUnwantedCallback, 2744 @NonNull Consumer<Integer> validationStatusCallback) { 2745 return new VcnNetworkAgent( 2746 vcnContext, 2747 tag, 2748 caps, 2749 lp, 2750 score, 2751 nac, 2752 provider, 2753 networkUnwantedCallback, 2754 validationStatusCallback); 2755 } 2756 2757 /** Checks if airplane mode is enabled. */ isAirplaneModeOn(@onNull VcnContext vcnContext)2758 public boolean isAirplaneModeOn(@NonNull VcnContext vcnContext) { 2759 return Settings.Global.getInt(vcnContext.getContext().getContentResolver(), 2760 Settings.Global.AIRPLANE_MODE_ON, 0) != 0; 2761 } 2762 2763 /** Gets the elapsed real time since boot, in millis. */ getElapsedRealTime()2764 public long getElapsedRealTime() { 2765 return SystemClock.elapsedRealtime(); 2766 } 2767 2768 /** Gets the MTU for the given underlying interface. */ getUnderlyingIfaceMtu(String ifaceName)2769 public int getUnderlyingIfaceMtu(String ifaceName) { 2770 try { 2771 final NetworkInterface underlyingIface = NetworkInterface.getByName(ifaceName); 2772 return underlyingIface == null ? 0 : underlyingIface.getMTU(); 2773 } catch (IOException e) { 2774 Slog.d(TAG, "Could not get MTU of underlying network", e); 2775 return 0; 2776 } 2777 } 2778 2779 /** Gets the max number of parallel tunnels allowed for tunnel aggregation. */ getParallelTunnelCount( TelephonySubscriptionSnapshot snapshot, ParcelUuid subGrp)2780 public int getParallelTunnelCount( 2781 TelephonySubscriptionSnapshot snapshot, ParcelUuid subGrp) { 2782 PersistableBundleWrapper carrierConfig = snapshot.getCarrierConfigForSubGrp(subGrp); 2783 int result = TUNNEL_AGGREGATION_SA_COUNT_MAX_DEFAULT; 2784 2785 if (carrierConfig != null) { 2786 result = 2787 carrierConfig.getInt( 2788 VcnManager.VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY, 2789 TUNNEL_AGGREGATION_SA_COUNT_MAX_DEFAULT); 2790 } 2791 2792 // Guard against tunnel count < 1 2793 return Math.max(1, result); 2794 } 2795 } 2796 2797 /** 2798 * Proxy implementation of Child Session Configuration, used for testing. 2799 * 2800 * <p>This wrapper allows mocking of the final, parcelable ChildSessionConfiguration object for 2801 * testing purposes. This is the unfortunate result of mockito-inline (for mocking final 2802 * classes) not working properly with system services & associated classes. 2803 * 2804 * <p>This class MUST EXCLUSIVELY be a passthrough, proxying calls directly to the actual 2805 * ChildSessionConfiguration. 2806 */ 2807 @VisibleForTesting(visibility = Visibility.PRIVATE) 2808 public static class VcnChildSessionConfiguration { 2809 private final ChildSessionConfiguration mChildConfig; 2810 VcnChildSessionConfiguration(ChildSessionConfiguration childConfig)2811 public VcnChildSessionConfiguration(ChildSessionConfiguration childConfig) { 2812 mChildConfig = childConfig; 2813 } 2814 2815 /** Retrieves the addresses to be used inside the tunnel. */ getInternalAddresses()2816 public List<LinkAddress> getInternalAddresses() { 2817 return mChildConfig.getInternalAddresses(); 2818 } 2819 2820 /** Retrieves the DNS servers to be used inside the tunnel. */ getInternalDnsServers()2821 public List<InetAddress> getInternalDnsServers() { 2822 return mChildConfig.getInternalDnsServers(); 2823 } 2824 } 2825 2826 /** Proxy implementation of IKE session, used for testing. */ 2827 @VisibleForTesting(visibility = Visibility.PRIVATE) 2828 public static class VcnIkeSession { 2829 private final IkeSession mImpl; 2830 VcnIkeSession( VcnContext vcnContext, IkeSessionParams ikeSessionParams, ChildSessionParams childSessionParams, IkeSessionCallback ikeSessionCallback, ChildSessionCallback childSessionCallback)2831 public VcnIkeSession( 2832 VcnContext vcnContext, 2833 IkeSessionParams ikeSessionParams, 2834 ChildSessionParams childSessionParams, 2835 IkeSessionCallback ikeSessionCallback, 2836 ChildSessionCallback childSessionCallback) { 2837 mImpl = 2838 new IkeSession( 2839 vcnContext.getContext(), 2840 ikeSessionParams, 2841 childSessionParams, 2842 new HandlerExecutor(new Handler(vcnContext.getLooper())), 2843 ikeSessionCallback, 2844 childSessionCallback); 2845 } 2846 2847 /** Creates a new IKE Child session. */ openChildSession( @onNull ChildSessionParams childSessionParams, @NonNull ChildSessionCallback childSessionCallback)2848 public void openChildSession( 2849 @NonNull ChildSessionParams childSessionParams, 2850 @NonNull ChildSessionCallback childSessionCallback) { 2851 mImpl.openChildSession(childSessionParams, childSessionCallback); 2852 } 2853 2854 /** Closes an IKE session as identified by the ChildSessionCallback. */ closeChildSession(@onNull ChildSessionCallback childSessionCallback)2855 public void closeChildSession(@NonNull ChildSessionCallback childSessionCallback) { 2856 mImpl.closeChildSession(childSessionCallback); 2857 } 2858 2859 /** Gracefully closes this IKE Session, waiting for remote acknowledgement. */ close()2860 public void close() { 2861 mImpl.close(); 2862 } 2863 2864 /** Forcibly kills this IKE Session, without waiting for a closure confirmation. */ kill()2865 public void kill() { 2866 mImpl.kill(); 2867 } 2868 2869 /** Sets the underlying network used by the IkeSession. */ setNetwork(@onNull Network network)2870 public void setNetwork(@NonNull Network network) { 2871 mImpl.setNetwork(network); 2872 } 2873 } 2874 2875 /** Proxy Implementation of WakeLock, used for testing. */ 2876 @VisibleForTesting(visibility = Visibility.PRIVATE) 2877 public static class VcnWakeLock { 2878 private final WakeLock mImpl; 2879 VcnWakeLock(@onNull Context context, int flags, @NonNull String tag)2880 public VcnWakeLock(@NonNull Context context, int flags, @NonNull String tag) { 2881 final PowerManager powerManager = context.getSystemService(PowerManager.class); 2882 mImpl = powerManager.newWakeLock(flags, tag); 2883 mImpl.setReferenceCounted(false /* isReferenceCounted */); 2884 } 2885 2886 /** 2887 * Acquire this WakeLock. 2888 * 2889 * <p>Synchronize this action to minimize locking around WakeLock use. 2890 */ acquire()2891 public synchronized void acquire() { 2892 mImpl.acquire(); 2893 } 2894 2895 /** 2896 * Release this Wakelock. 2897 * 2898 * <p>Synchronize this action to minimize locking around WakeLock use. 2899 */ release()2900 public synchronized void release() { 2901 mImpl.release(); 2902 } 2903 } 2904 2905 /** Proxy Implementation of NetworkAgent, used for testing. */ 2906 @VisibleForTesting(visibility = Visibility.PRIVATE) 2907 public static class VcnNetworkAgent { 2908 private final NetworkAgent mImpl; 2909 VcnNetworkAgent( @onNull VcnContext vcnContext, @NonNull String tag, @NonNull NetworkCapabilities caps, @NonNull LinkProperties lp, @NonNull NetworkScore score, @NonNull NetworkAgentConfig nac, @NonNull NetworkProvider provider, @NonNull Consumer<VcnNetworkAgent> networkUnwantedCallback, @NonNull Consumer<Integer> validationStatusCallback)2910 public VcnNetworkAgent( 2911 @NonNull VcnContext vcnContext, 2912 @NonNull String tag, 2913 @NonNull NetworkCapabilities caps, 2914 @NonNull LinkProperties lp, 2915 @NonNull NetworkScore score, 2916 @NonNull NetworkAgentConfig nac, 2917 @NonNull NetworkProvider provider, 2918 @NonNull Consumer<VcnNetworkAgent> networkUnwantedCallback, 2919 @NonNull Consumer<Integer> validationStatusCallback) { 2920 mImpl = 2921 new NetworkAgent( 2922 vcnContext.getContext(), 2923 vcnContext.getLooper(), 2924 tag, 2925 caps, 2926 lp, 2927 score, 2928 nac, 2929 provider) { 2930 @Override 2931 public void onNetworkUnwanted() { 2932 networkUnwantedCallback.accept(VcnNetworkAgent.this); 2933 } 2934 2935 @Override 2936 public void onValidationStatus(int status, @Nullable Uri redirectUri) { 2937 validationStatusCallback.accept(status); 2938 } 2939 }; 2940 } 2941 2942 /** Registers the underlying NetworkAgent */ register()2943 public void register() { 2944 mImpl.register(); 2945 } 2946 2947 /** Marks the underlying NetworkAgent as connected */ markConnected()2948 public void markConnected() { 2949 mImpl.markConnected(); 2950 } 2951 2952 /** Unregisters the underlying NetworkAgent */ unregister()2953 public void unregister() { 2954 mImpl.unregister(); 2955 } 2956 2957 /** Sends new NetworkCapabilities for the underlying NetworkAgent */ sendNetworkCapabilities(@onNull NetworkCapabilities caps)2958 public void sendNetworkCapabilities(@NonNull NetworkCapabilities caps) { 2959 mImpl.sendNetworkCapabilities(caps); 2960 } 2961 2962 /** Sends new LinkProperties for the underlying NetworkAgent */ sendLinkProperties(@onNull LinkProperties lp)2963 public void sendLinkProperties(@NonNull LinkProperties lp) { 2964 mImpl.sendLinkProperties(lp); 2965 } 2966 2967 /** Sends new NetworkCapabilities for the underlying NetworkAgent */ setUnderlyingNetworks(@ullable List<Network> underlyingNetworks)2968 public void setUnderlyingNetworks(@Nullable List<Network> underlyingNetworks) { 2969 mImpl.setUnderlyingNetworks(underlyingNetworks); 2970 } 2971 2972 /** Retrieves the Network for the underlying NetworkAgent */ 2973 @Nullable getNetwork()2974 public Network getNetwork() { 2975 return mImpl.getNetwork(); 2976 } 2977 } 2978 } 2979