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 if (!UnderlyingNetworkRecord.isSameNetwork(mUnderlying, underlying)) { 919 logInfo( 920 "Selected underlying network changed: " 921 + (underlying == null ? null : underlying.network)); 922 } 923 924 // TODO(b/179091925): Move the delayed-message handling to BaseState 925 926 // If underlying is null, all underlying networks have been lost. Disconnect VCN after a 927 // timeout (or immediately if in airplane mode, since the device user has indicated that 928 // the radios should all be turned off). 929 if (underlying == null) { 930 if (mDeps.isAirplaneModeOn(mVcnContext)) { 931 sendMessageAndAcquireWakeLock( 932 EVENT_UNDERLYING_NETWORK_CHANGED, 933 TOKEN_ALL, 934 new EventUnderlyingNetworkChangedInfo(null)); 935 sendDisconnectRequestedAndAcquireWakelock( 936 DISCONNECT_REASON_UNDERLYING_NETWORK_LOST, false /* shouldQuit */); 937 return; 938 } 939 940 setDisconnectRequestAlarm(); 941 } else { 942 // Received a new Network so any previous alarm is irrelevant - cancel + clear it, 943 // and cancel any queued EVENT_DISCONNECT_REQUEST messages 944 cancelDisconnectRequestAlarm(); 945 } 946 947 sendMessageAndAcquireWakeLock( 948 EVENT_UNDERLYING_NETWORK_CHANGED, 949 TOKEN_ALL, 950 new EventUnderlyingNetworkChangedInfo(underlying)); 951 } 952 } 953 acquireWakeLock()954 private void acquireWakeLock() { 955 mVcnContext.ensureRunningOnLooperThread(); 956 957 if (!mIsQuitting.getValue()) { 958 mWakeLock.acquire(); 959 960 logVdbg("Wakelock acquired: " + mWakeLock); 961 } 962 } 963 releaseWakeLock()964 private void releaseWakeLock() { 965 mVcnContext.ensureRunningOnLooperThread(); 966 967 mWakeLock.release(); 968 969 logVdbg("Wakelock released: " + mWakeLock); 970 } 971 972 /** 973 * Attempt to release mWakeLock - this can only be done if the Handler is null (meaning the 974 * StateMachine has been shutdown and thus has no business keeping the WakeLock) or if there are 975 * no more messags left to process in the Handler queue (at which point the WakeLock can be 976 * released until more messages must be processed). 977 */ maybeReleaseWakeLock()978 private void maybeReleaseWakeLock() { 979 final Handler handler = getHandler(); 980 if (handler == null || !handler.hasMessagesOrCallbacks()) { 981 releaseWakeLock(); 982 } 983 } 984 985 @Override sendMessage(int what)986 public void sendMessage(int what) { 987 logWtf( 988 "sendMessage should not be used in VcnGatewayConnection. See" 989 + " sendMessageAndAcquireWakeLock()"); 990 super.sendMessage(what); 991 } 992 993 @Override sendMessage(int what, Object obj)994 public void sendMessage(int what, Object obj) { 995 logWtf( 996 "sendMessage should not be used in VcnGatewayConnection. See" 997 + " sendMessageAndAcquireWakeLock()"); 998 super.sendMessage(what, obj); 999 } 1000 1001 @Override sendMessage(int what, int arg1)1002 public void sendMessage(int what, int arg1) { 1003 logWtf( 1004 "sendMessage should not be used in VcnGatewayConnection. See" 1005 + " sendMessageAndAcquireWakeLock()"); 1006 super.sendMessage(what, arg1); 1007 } 1008 1009 @Override sendMessage(int what, int arg1, int arg2)1010 public void sendMessage(int what, int arg1, int arg2) { 1011 logWtf( 1012 "sendMessage should not be used in VcnGatewayConnection. See" 1013 + " sendMessageAndAcquireWakeLock()"); 1014 super.sendMessage(what, arg1, arg2); 1015 } 1016 1017 @Override sendMessage(int what, int arg1, int arg2, Object obj)1018 public void sendMessage(int what, int arg1, int arg2, Object obj) { 1019 logWtf( 1020 "sendMessage should not be used in VcnGatewayConnection. See" 1021 + " sendMessageAndAcquireWakeLock()"); 1022 super.sendMessage(what, arg1, arg2, obj); 1023 } 1024 1025 @Override sendMessage(Message msg)1026 public void sendMessage(Message msg) { 1027 logWtf( 1028 "sendMessage should not be used in VcnGatewayConnection. See" 1029 + " sendMessageAndAcquireWakeLock()"); 1030 super.sendMessage(msg); 1031 } 1032 1033 // TODO(b/180146061): also override and Log.wtf() other Message handling methods 1034 // In mind are sendMessageDelayed(), sendMessageAtFrontOfQueue, removeMessages, and 1035 // removeDeferredMessages 1036 1037 /** 1038 * WakeLock-based alternative to {@link #sendMessage}. Use to guarantee that the device will not 1039 * go to sleep before processing the sent message. 1040 */ sendMessageAndAcquireWakeLock(int what, int token)1041 private void sendMessageAndAcquireWakeLock(int what, int token) { 1042 acquireWakeLock(); 1043 super.sendMessage(what, token); 1044 } 1045 1046 /** 1047 * WakeLock-based alternative to {@link #sendMessage}. Use to guarantee that the device will not 1048 * go to sleep before processing the sent message. 1049 */ sendMessageAndAcquireWakeLock(int what, int token, EventInfo data)1050 private void sendMessageAndAcquireWakeLock(int what, int token, EventInfo data) { 1051 acquireWakeLock(); 1052 super.sendMessage(what, token, ARG_NOT_PRESENT, data); 1053 } 1054 1055 /** 1056 * WakeLock-based alternative to {@link #sendMessage}. Use to guarantee that the device will not 1057 * go to sleep before processing the sent message. 1058 */ sendMessageAndAcquireWakeLock(int what, int token, int arg2, EventInfo data)1059 private void sendMessageAndAcquireWakeLock(int what, int token, int arg2, EventInfo data) { 1060 acquireWakeLock(); 1061 super.sendMessage(what, token, arg2, data); 1062 } 1063 1064 /** 1065 * WakeLock-based alternative to {@link #sendMessage}. Use to guarantee that the device will not 1066 * go to sleep before processing the sent message. 1067 */ sendMessageAndAcquireWakeLock(Message msg)1068 private void sendMessageAndAcquireWakeLock(Message msg) { 1069 acquireWakeLock(); 1070 super.sendMessage(msg); 1071 } 1072 1073 /** 1074 * Removes all messages matching the given parameters, and attempts to release mWakeLock if the 1075 * Handler is empty. 1076 * 1077 * @param what the Message.what value to be removed 1078 */ removeEqualMessages(int what)1079 private void removeEqualMessages(int what) { 1080 removeEqualMessages(what, null /* obj */); 1081 } 1082 1083 /** 1084 * Removes all messages matching the given parameters, and attempts to release mWakeLock if the 1085 * Handler is empty. 1086 * 1087 * @param what the Message.what value to be removed 1088 * @param obj the Message.obj to to be removed, or null if all messages matching Message.what 1089 * should be removed 1090 */ removeEqualMessages(int what, @Nullable Object obj)1091 private void removeEqualMessages(int what, @Nullable Object obj) { 1092 final Handler handler = getHandler(); 1093 if (handler != null) { 1094 handler.removeEqualMessages(what, obj); 1095 } 1096 1097 maybeReleaseWakeLock(); 1098 } 1099 createScheduledAlarm( @onNull String cmdName, Message delayedMessage, long delay)1100 private WakeupMessage createScheduledAlarm( 1101 @NonNull String cmdName, Message delayedMessage, long delay) { 1102 final Handler handler = getHandler(); 1103 if (handler == null) { 1104 logWarn( 1105 "Attempted to schedule alarm after StateMachine has quit", 1106 new IllegalStateException()); 1107 return null; // StateMachine has already quit. 1108 } 1109 1110 // WakeupMessage uses Handler#dispatchMessage() to immediately handle the specified Runnable 1111 // at the scheduled time. dispatchMessage() immediately executes and there may be queued 1112 // events that resolve the scheduled alarm pending in the queue. So, use the Runnable to 1113 // place the alarm event at the end of the queue with sendMessageAndAcquireWakeLock (which 1114 // guarantees the device will stay awake). 1115 final WakeupMessage alarm = 1116 mDeps.newWakeupMessage( 1117 mVcnContext, 1118 handler, 1119 cmdName, 1120 () -> sendMessageAndAcquireWakeLock(delayedMessage)); 1121 alarm.schedule(mDeps.getElapsedRealTime() + delay); 1122 return alarm; 1123 } 1124 setTeardownTimeoutAlarm()1125 private void setTeardownTimeoutAlarm() { 1126 logVdbg("Setting teardown timeout alarm; mCurrentToken: " + mCurrentToken); 1127 1128 // Safe to assign this alarm because it is either 1) already null, or 2) already fired. In 1129 // either case, there is nothing to cancel. 1130 if (mTeardownTimeoutAlarm != null) { 1131 logWtf( 1132 "mTeardownTimeoutAlarm should be null before being set; mCurrentToken: " 1133 + mCurrentToken); 1134 } 1135 1136 final Message delayedMessage = obtainMessage(EVENT_TEARDOWN_TIMEOUT_EXPIRED, mCurrentToken); 1137 mTeardownTimeoutAlarm = 1138 createScheduledAlarm( 1139 TEARDOWN_TIMEOUT_ALARM, 1140 delayedMessage, 1141 TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS)); 1142 } 1143 cancelTeardownTimeoutAlarm()1144 private void cancelTeardownTimeoutAlarm() { 1145 logVdbg("Cancelling teardown timeout alarm; mCurrentToken: " + mCurrentToken); 1146 1147 if (mTeardownTimeoutAlarm != null) { 1148 mTeardownTimeoutAlarm.cancel(); 1149 mTeardownTimeoutAlarm = null; 1150 } 1151 1152 // Cancel any existing teardown timeouts 1153 removeEqualMessages(EVENT_TEARDOWN_TIMEOUT_EXPIRED); 1154 } 1155 setDisconnectRequestAlarm()1156 private void setDisconnectRequestAlarm() { 1157 logVdbg( 1158 "Setting alarm to disconnect due to underlying network loss;" 1159 + " mCurrentToken: " 1160 + mCurrentToken); 1161 1162 // Only schedule a NEW alarm if none is already set. 1163 if (mDisconnectRequestAlarm != null) { 1164 return; 1165 } 1166 1167 final Message delayedMessage = 1168 obtainMessage( 1169 EVENT_DISCONNECT_REQUESTED, 1170 TOKEN_ALL, 1171 0 /* arg2 */, 1172 new EventDisconnectRequestedInfo( 1173 DISCONNECT_REASON_UNDERLYING_NETWORK_LOST, false /* shouldQuit */)); 1174 mDisconnectRequestAlarm = 1175 createScheduledAlarm( 1176 DISCONNECT_REQUEST_ALARM, 1177 delayedMessage, 1178 TimeUnit.SECONDS.toMillis(NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS)); 1179 } 1180 cancelDisconnectRequestAlarm()1181 private void cancelDisconnectRequestAlarm() { 1182 logVdbg( 1183 "Cancelling alarm to disconnect due to underlying network loss;" 1184 + " mCurrentToken: " 1185 + mCurrentToken); 1186 1187 if (mDisconnectRequestAlarm != null) { 1188 mDisconnectRequestAlarm.cancel(); 1189 mDisconnectRequestAlarm = null; 1190 } 1191 1192 // Cancel any existing disconnect due to previous loss of underlying network 1193 removeEqualMessages( 1194 EVENT_DISCONNECT_REQUESTED, 1195 new EventDisconnectRequestedInfo( 1196 DISCONNECT_REASON_UNDERLYING_NETWORK_LOST, false /* shouldQuit */)); 1197 } 1198 setRetryTimeoutAlarm(long delay)1199 private void setRetryTimeoutAlarm(long delay) { 1200 logVdbg("Setting retry alarm; mCurrentToken: " + mCurrentToken); 1201 1202 // Safe to assign this alarm because it is either 1) already null, or 2) already fired. In 1203 // either case, there is nothing to cancel. 1204 if (mRetryTimeoutAlarm != null) { 1205 logWtf( 1206 "mRetryTimeoutAlarm should be null before being set; mCurrentToken: " 1207 + mCurrentToken); 1208 } 1209 1210 final Message delayedMessage = obtainMessage(EVENT_RETRY_TIMEOUT_EXPIRED, mCurrentToken); 1211 mRetryTimeoutAlarm = createScheduledAlarm(RETRY_TIMEOUT_ALARM, delayedMessage, delay); 1212 } 1213 cancelRetryTimeoutAlarm()1214 private void cancelRetryTimeoutAlarm() { 1215 logVdbg("Cancel retry alarm; mCurrentToken: " + mCurrentToken); 1216 1217 if (mRetryTimeoutAlarm != null) { 1218 mRetryTimeoutAlarm.cancel(); 1219 mRetryTimeoutAlarm = null; 1220 } 1221 1222 removeEqualMessages(EVENT_RETRY_TIMEOUT_EXPIRED); 1223 } 1224 1225 @VisibleForTesting(visibility = Visibility.PRIVATE) setSafeModeAlarm()1226 void setSafeModeAlarm() { 1227 final boolean isFlagSafeModeConfigEnabled = mVcnContext.getFeatureFlags().safeModeConfig(); 1228 logVdbg("isFlagSafeModeConfigEnabled " + isFlagSafeModeConfigEnabled); 1229 1230 if (isFlagSafeModeConfigEnabled && !mConnectionConfig.isSafeModeEnabled()) { 1231 logVdbg("setSafeModeAlarm: safe mode disabled"); 1232 return; 1233 } 1234 1235 logVdbg("Setting safe mode alarm; mCurrentToken: " + mCurrentToken); 1236 1237 // Only schedule a NEW alarm if none is already set. 1238 if (mSafeModeTimeoutAlarm != null) { 1239 return; 1240 } 1241 1242 final Message delayedMessage = obtainMessage(EVENT_SAFE_MODE_TIMEOUT_EXCEEDED, TOKEN_ALL); 1243 mSafeModeTimeoutAlarm = 1244 createScheduledAlarm( 1245 SAFEMODE_TIMEOUT_ALARM, 1246 delayedMessage, 1247 getSafeModeTimeoutMs(mVcnContext, mLastSnapshot, mSubscriptionGroup)); 1248 } 1249 1250 /** Gets the safe mode timeout */ 1251 @VisibleForTesting(visibility = Visibility.PRIVATE) getSafeModeTimeoutMs( VcnContext vcnContext, TelephonySubscriptionSnapshot snapshot, ParcelUuid subGrp)1252 public static long getSafeModeTimeoutMs( 1253 VcnContext vcnContext, TelephonySubscriptionSnapshot snapshot, ParcelUuid subGrp) { 1254 final int defaultSeconds = 1255 vcnContext.isInTestMode() 1256 ? SAFEMODE_TIMEOUT_SECONDS_TEST_MODE 1257 : SAFEMODE_TIMEOUT_SECONDS; 1258 1259 final PersistableBundleWrapper carrierConfig = snapshot.getCarrierConfigForSubGrp(subGrp); 1260 int resultSeconds = defaultSeconds; 1261 1262 if (vcnContext.isFlagSafeModeTimeoutConfigEnabled() && carrierConfig != null) { 1263 resultSeconds = 1264 carrierConfig.getInt( 1265 VcnManager.VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY, defaultSeconds); 1266 } 1267 1268 return TimeUnit.SECONDS.toMillis(resultSeconds); 1269 } 1270 cancelSafeModeAlarm()1271 private void cancelSafeModeAlarm() { 1272 logVdbg("Cancel safe mode alarm; mCurrentToken: " + mCurrentToken); 1273 1274 if (mSafeModeTimeoutAlarm != null) { 1275 mSafeModeTimeoutAlarm.cancel(); 1276 mSafeModeTimeoutAlarm = null; 1277 } 1278 1279 removeEqualMessages(EVENT_SAFE_MODE_TIMEOUT_EXCEEDED); 1280 } 1281 sessionLostWithoutCallback(int token, @Nullable Exception exception)1282 private void sessionLostWithoutCallback(int token, @Nullable Exception exception) { 1283 sendMessageAndAcquireWakeLock( 1284 EVENT_SESSION_LOST, token, new EventSessionLostInfo(exception)); 1285 } 1286 sessionLost(int token, @Nullable Exception exception)1287 private void sessionLost(int token, @Nullable Exception exception) { 1288 // Only notify mGatewayStatusCallback if the session was lost with an error. All 1289 // authentication and DNS failures are sent through 1290 // IkeSessionCallback.onClosedExceptionally(), which calls sessionClosed() 1291 if (exception != null) { 1292 mGatewayStatusCallback.onGatewayConnectionError( 1293 mConnectionConfig.getGatewayConnectionName(), 1294 VCN_ERROR_CODE_INTERNAL_ERROR, 1295 RuntimeException.class.getName(), 1296 "Received " 1297 + exception.getClass().getSimpleName() 1298 + " with message: " 1299 + exception.getMessage()); 1300 } 1301 1302 sessionLostWithoutCallback(token, exception); 1303 } 1304 isIkeAuthFailure(@onNull Exception exception)1305 private static boolean isIkeAuthFailure(@NonNull Exception exception) { 1306 if (!(exception instanceof IkeProtocolException)) { 1307 return false; 1308 } 1309 1310 return ((IkeProtocolException) exception).getErrorType() 1311 == ERROR_TYPE_AUTHENTICATION_FAILED; 1312 } 1313 notifyStatusCallbackForSessionClosed(@onNull Exception exception)1314 private void notifyStatusCallbackForSessionClosed(@NonNull Exception exception) { 1315 final int errorCode; 1316 final String exceptionClass; 1317 final String exceptionMessage; 1318 1319 if (isIkeAuthFailure(exception)) { 1320 errorCode = VCN_ERROR_CODE_CONFIG_ERROR; 1321 exceptionClass = exception.getClass().getName(); 1322 exceptionMessage = exception.getMessage(); 1323 } else if (exception instanceof IkeInternalException 1324 && exception.getCause() instanceof IOException) { 1325 errorCode = VCN_ERROR_CODE_NETWORK_ERROR; 1326 exceptionClass = IOException.class.getName(); 1327 exceptionMessage = exception.getCause().getMessage(); 1328 } else { 1329 errorCode = VCN_ERROR_CODE_INTERNAL_ERROR; 1330 exceptionClass = RuntimeException.class.getName(); 1331 exceptionMessage = 1332 "Received " 1333 + exception.getClass().getSimpleName() 1334 + " with message: " 1335 + exception.getMessage(); 1336 } 1337 1338 logDbg( 1339 "Encountered error; code=" 1340 + errorCode 1341 + ", exceptionClass=" 1342 + exceptionClass 1343 + ", exceptionMessage=" 1344 + exceptionMessage); 1345 1346 mGatewayStatusCallback.onGatewayConnectionError( 1347 mConnectionConfig.getGatewayConnectionName(), 1348 errorCode, 1349 exceptionClass, 1350 exceptionMessage); 1351 } 1352 ikeConnectionInfoChanged( int token, @NonNull IkeSessionConnectionInfo ikeConnectionInfo)1353 private void ikeConnectionInfoChanged( 1354 int token, @NonNull IkeSessionConnectionInfo ikeConnectionInfo) { 1355 sendMessageAndAcquireWakeLock( 1356 EVENT_IKE_CONNECTION_INFO_CHANGED, 1357 token, 1358 new EventIkeConnectionInfoChangedInfo(ikeConnectionInfo)); 1359 } 1360 sessionClosed(int token, @Nullable Exception exception)1361 private void sessionClosed(int token, @Nullable Exception exception) { 1362 if (exception != null) { 1363 notifyStatusCallbackForSessionClosed(exception); 1364 } 1365 1366 // SESSION_LOST MUST be sent before SESSION_CLOSED to ensure that the SM moves to the 1367 // Disconnecting state. 1368 sessionLostWithoutCallback(token, exception); 1369 sendMessageAndAcquireWakeLock(EVENT_SESSION_CLOSED, token); 1370 } 1371 migrationCompleted( int token, @NonNull IpSecTransform inTransform, @NonNull IpSecTransform outTransform)1372 private void migrationCompleted( 1373 int token, @NonNull IpSecTransform inTransform, @NonNull IpSecTransform outTransform) { 1374 sendMessageAndAcquireWakeLock( 1375 EVENT_MIGRATION_COMPLETED, 1376 token, 1377 new EventMigrationCompletedInfo(inTransform, outTransform)); 1378 } 1379 childTransformCreated( int token, @NonNull IpSecTransform transform, int direction)1380 private void childTransformCreated( 1381 int token, @NonNull IpSecTransform transform, int direction) { 1382 sendMessageAndAcquireWakeLock( 1383 EVENT_TRANSFORM_CREATED, 1384 token, 1385 new EventTransformCreatedInfo(direction, transform)); 1386 } 1387 childOpened(int token, @NonNull VcnChildSessionConfiguration childConfig)1388 private void childOpened(int token, @NonNull VcnChildSessionConfiguration childConfig) { 1389 sendMessageAndAcquireWakeLock( 1390 EVENT_SETUP_COMPLETED, token, new EventSetupCompletedInfo(childConfig)); 1391 } 1392 1393 private abstract class BaseState extends State { 1394 @Override enter()1395 public void enter() { 1396 try { 1397 enterState(); 1398 } catch (Exception e) { 1399 logWtf("Uncaught exception", e); 1400 sendDisconnectRequestedAndAcquireWakelock( 1401 DISCONNECT_REASON_INTERNAL_ERROR + e.toString(), true /* shouldQuit */); 1402 } 1403 } 1404 enterState()1405 protected void enterState() throws Exception {} 1406 1407 /** 1408 * Returns whether the given token is valid. 1409 * 1410 * <p>By default, States consider any and all token to be 'valid'. 1411 * 1412 * <p>States should override this method if they want to restrict message handling to 1413 * specific tokens. 1414 */ isValidToken(int token)1415 protected boolean isValidToken(int token) { 1416 return true; 1417 } 1418 1419 /** 1420 * Top-level processMessage with safeguards to prevent crashing the System Server on non-eng 1421 * builds. 1422 * 1423 * <p>Here be dragons: processMessage() is final to ensure that mWakeLock is released once 1424 * the Handler queue is empty. Future changes (or overrides) to processMessage() to MUST 1425 * ensure that mWakeLock is correctly released. 1426 */ 1427 @Override processMessage(Message msg)1428 public final boolean processMessage(Message msg) { 1429 final int token = msg.arg1; 1430 if (!isValidToken(token)) { 1431 logDbg("Message called with obsolete token: " + token + "; what: " + msg.what); 1432 return HANDLED; 1433 } 1434 1435 try { 1436 processStateMsg(msg); 1437 } catch (Exception e) { 1438 logWtf("Uncaught exception", e); 1439 sendDisconnectRequestedAndAcquireWakelock( 1440 DISCONNECT_REASON_INTERNAL_ERROR + e.toString(), true /* shouldQuit */); 1441 } 1442 1443 // Attempt to release the WakeLock - only possible if the Handler queue is empty 1444 maybeReleaseWakeLock(); 1445 1446 return HANDLED; 1447 } 1448 processStateMsg(Message msg)1449 protected abstract void processStateMsg(Message msg) throws Exception; 1450 1451 @Override exit()1452 public void exit() { 1453 try { 1454 exitState(); 1455 } catch (Exception e) { 1456 logWtf("Uncaught exception", e); 1457 sendDisconnectRequestedAndAcquireWakelock( 1458 DISCONNECT_REASON_INTERNAL_ERROR + e.toString(), true /* shouldQuit */); 1459 } 1460 } 1461 exitState()1462 protected void exitState() throws Exception {} 1463 logUnhandledMessage(Message msg)1464 protected void logUnhandledMessage(Message msg) { 1465 // Log as unexpected all known messages, and log all else as unknown. 1466 switch (msg.what) { 1467 case EVENT_UNDERLYING_NETWORK_CHANGED: // Fallthrough 1468 case EVENT_RETRY_TIMEOUT_EXPIRED: // Fallthrough 1469 case EVENT_SESSION_LOST: // Fallthrough 1470 case EVENT_SESSION_CLOSED: // Fallthrough 1471 case EVENT_TRANSFORM_CREATED: // Fallthrough 1472 case EVENT_SETUP_COMPLETED: // Fallthrough 1473 case EVENT_DISCONNECT_REQUESTED: // Fallthrough 1474 case EVENT_TEARDOWN_TIMEOUT_EXPIRED: // Fallthrough 1475 case EVENT_SUBSCRIPTIONS_CHANGED: // Fallthrough 1476 case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED: // Fallthrough 1477 case EVENT_MIGRATION_COMPLETED: // Fallthrough 1478 case EVENT_IKE_CONNECTION_INFO_CHANGED: // Fallthrough 1479 case EVENT_DATA_STALL_SUSPECTED: 1480 logUnexpectedEvent(msg.what); 1481 break; 1482 default: 1483 logWtfUnknownEvent(msg.what); 1484 break; 1485 } 1486 } 1487 teardownNetwork()1488 protected void teardownNetwork() { 1489 if (mNetworkAgent != null) { 1490 mNetworkAgent.unregister(); 1491 mNetworkAgent = null; 1492 } 1493 } 1494 handleDisconnectRequested(EventDisconnectRequestedInfo info)1495 protected void handleDisconnectRequested(EventDisconnectRequestedInfo info) { 1496 // TODO(b/180526152): notify VcnStatusCallback for Network loss 1497 1498 logInfo("Tearing down. Cause: " + info.reason + "; quitting = " + info.shouldQuit); 1499 if (info.shouldQuit) { 1500 mIsQuitting.setTrue(); 1501 } 1502 1503 teardownNetwork(); 1504 1505 if (mIkeSession == null) { 1506 // Already disconnected, go straight to DisconnectedState 1507 transitionTo(mDisconnectedState); 1508 } else { 1509 // Still need to wait for full closure 1510 transitionTo(mDisconnectingState); 1511 } 1512 } 1513 handleSafeModeTimeoutExceeded()1514 protected void handleSafeModeTimeoutExceeded() { 1515 mSafeModeTimeoutAlarm = null; 1516 logInfo("Entering safe mode after timeout exceeded"); 1517 1518 // Connectivity for this GatewayConnection is broken; tear down the Network. 1519 teardownNetwork(); 1520 mIsInSafeMode = true; 1521 mGatewayStatusCallback.onSafeModeStatusChanged(); 1522 } 1523 logUnexpectedEvent(int what)1524 protected void logUnexpectedEvent(int what) { 1525 logVdbg( 1526 "Unexpected event code " 1527 + what 1528 + " in state " 1529 + this.getClass().getSimpleName()); 1530 } 1531 logWtfUnknownEvent(int what)1532 protected void logWtfUnknownEvent(int what) { 1533 logWtf("Unknown event code " + what + " in state " + this.getClass().getSimpleName()); 1534 } 1535 } 1536 1537 /** 1538 * State representing the a disconnected VCN tunnel. 1539 * 1540 * <p>This is also is the initial state. 1541 */ 1542 private class DisconnectedState extends BaseState { 1543 @Override enterState()1544 protected void enterState() { 1545 if (mIsQuitting.getValue()) { 1546 quitNow(); // Ignore all queued events; cleanup is complete. 1547 } 1548 1549 if (mIkeSession != null || mNetworkAgent != null) { 1550 logWtf("Active IKE Session or NetworkAgent in DisconnectedState"); 1551 } 1552 1553 cancelSafeModeAlarm(); 1554 } 1555 1556 @Override processStateMsg(Message msg)1557 protected void processStateMsg(Message msg) { 1558 switch (msg.what) { 1559 case EVENT_UNDERLYING_NETWORK_CHANGED: 1560 // First network found; start tunnel 1561 mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying; 1562 1563 if (mUnderlying != null) { 1564 transitionTo(mConnectingState); 1565 } 1566 break; 1567 case EVENT_DISCONNECT_REQUESTED: 1568 if (((EventDisconnectRequestedInfo) msg.obj).shouldQuit) { 1569 mIsQuitting.setTrue(); 1570 1571 quitNow(); 1572 } 1573 break; 1574 default: 1575 logUnhandledMessage(msg); 1576 break; 1577 } 1578 } 1579 1580 @Override exitState()1581 protected void exitState() { 1582 // Safe to blindly set up, as it is cancelled and cleared on entering this state 1583 setSafeModeAlarm(); 1584 } 1585 } 1586 1587 private abstract class ActiveBaseState extends BaseState { 1588 @Override isValidToken(int token)1589 protected boolean isValidToken(int token) { 1590 return (token == TOKEN_ALL || token == mCurrentToken); 1591 } 1592 } 1593 1594 /** 1595 * Transitive state representing a VCN that is tearing down an IKE session. 1596 * 1597 * <p>In this state, the IKE session is in the process of being torn down. If the IKE session 1598 * does not complete teardown in a timely fashion, it will be killed (forcibly closed). 1599 */ 1600 private class DisconnectingState extends ActiveBaseState { 1601 /** 1602 * Whether to skip the RetryTimeoutState and go straight to the ConnectingState. 1603 * 1604 * <p>This is used when an underlying network change triggered a restart on a new network. 1605 * 1606 * <p>Reset (to false) upon exit of the DisconnectingState. 1607 */ 1608 private boolean mSkipRetryTimeout = false; 1609 1610 // TODO(b/178441390): Remove this in favor of resetting retry timers on UND_NET change. setSkipRetryTimeout(boolean shouldSkip)1611 public void setSkipRetryTimeout(boolean shouldSkip) { 1612 mSkipRetryTimeout = shouldSkip; 1613 } 1614 1615 @Override enterState()1616 protected void enterState() throws Exception { 1617 if (mIkeSession == null) { 1618 logWtf("IKE session was already closed when entering Disconnecting state."); 1619 sendMessageAndAcquireWakeLock(EVENT_SESSION_CLOSED, mCurrentToken); 1620 return; 1621 } 1622 1623 // If underlying network has already been lost, save some time and just kill the session 1624 if (mUnderlying == null) { 1625 // Will trigger a EVENT_SESSION_CLOSED as IkeSession shuts down. 1626 mIkeSession.kill(); 1627 return; 1628 } 1629 1630 mIkeSession.close(); 1631 1632 // Safe to blindly set up, as it is cancelled and cleared on exiting this state 1633 setTeardownTimeoutAlarm(); 1634 } 1635 1636 @Override processStateMsg(Message msg)1637 protected void processStateMsg(Message msg) { 1638 switch (msg.what) { 1639 case EVENT_UNDERLYING_NETWORK_CHANGED: // Fallthrough 1640 mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying; 1641 1642 // If we received a new underlying network, continue. 1643 if (mUnderlying != null) { 1644 break; 1645 } 1646 1647 // Fallthrough; no network exists to send IKE close session requests. 1648 case EVENT_TEARDOWN_TIMEOUT_EXPIRED: 1649 // Grace period ended. Kill session, triggering EVENT_SESSION_CLOSED 1650 mIkeSession.kill(); 1651 1652 break; 1653 case EVENT_DISCONNECT_REQUESTED: 1654 EventDisconnectRequestedInfo info = ((EventDisconnectRequestedInfo) msg.obj); 1655 if (info.shouldQuit) { 1656 mIsQuitting.setTrue(); 1657 } 1658 1659 teardownNetwork(); 1660 1661 if (info.reason.equals(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)) { 1662 // TODO(b/180526152): notify VcnStatusCallback for Network loss 1663 1664 // Will trigger EVENT_SESSION_CLOSED immediately. 1665 mIkeSession.kill(); 1666 break; 1667 } 1668 1669 // Otherwise we are already in the process of shutting down. 1670 break; 1671 case EVENT_SESSION_CLOSED: 1672 mIkeSession = null; 1673 1674 if (!mIsQuitting.getValue() && mUnderlying != null) { 1675 transitionTo(mSkipRetryTimeout ? mConnectingState : mRetryTimeoutState); 1676 } else { 1677 teardownNetwork(); 1678 transitionTo(mDisconnectedState); 1679 } 1680 break; 1681 case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED: 1682 handleSafeModeTimeoutExceeded(); 1683 break; 1684 default: 1685 logUnhandledMessage(msg); 1686 break; 1687 } 1688 } 1689 1690 @Override exitState()1691 protected void exitState() throws Exception { 1692 mSkipRetryTimeout = false; 1693 1694 cancelTeardownTimeoutAlarm(); 1695 } 1696 } 1697 1698 /** 1699 * Transitive state representing a VCN that is making an primary (non-handover) connection. 1700 * 1701 * <p>This state starts IKE negotiation, but defers transform application & network setup to the 1702 * Connected state. 1703 */ 1704 private class ConnectingState extends ActiveBaseState { 1705 @Override enterState()1706 protected void enterState() { 1707 if (mIkeSession != null) { 1708 logWtf("ConnectingState entered with active session"); 1709 1710 // Attempt to recover. 1711 mIkeSession.kill(); 1712 mIkeSession = null; 1713 } 1714 1715 mIkeSession = buildIkeSession(mUnderlying.network); 1716 } 1717 1718 @Override processStateMsg(Message msg)1719 protected void processStateMsg(Message msg) { 1720 switch (msg.what) { 1721 case EVENT_UNDERLYING_NETWORK_CHANGED: 1722 final UnderlyingNetworkRecord oldUnderlying = mUnderlying; 1723 mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying; 1724 1725 if (oldUnderlying == null) { 1726 // This should never happen, but if it does, there's likely a nasty bug. 1727 logWtf("Old underlying network was null in connected state. Bug?"); 1728 } 1729 1730 // If new underlying is null, all underlying networks have been lost; disconnect 1731 if (mUnderlying == null) { 1732 transitionTo(mDisconnectingState); 1733 break; 1734 } 1735 1736 if (oldUnderlying != null 1737 && mUnderlying.network.equals(oldUnderlying.network)) { 1738 break; // Only network properties have changed; continue connecting. 1739 } 1740 // Else, retry on the new network. 1741 1742 // Immediately come back to the ConnectingState (skip RetryTimeout, since this 1743 // isn't a failure) 1744 mDisconnectingState.setSkipRetryTimeout(true); 1745 1746 // fallthrough - disconnect, and retry on new network. 1747 case EVENT_SESSION_LOST: 1748 transitionTo(mDisconnectingState); 1749 break; 1750 case EVENT_SESSION_CLOSED: 1751 // Disconnecting state waits for EVENT_SESSION_CLOSED to shutdown, and this 1752 // message may not be posted again. Defer to ensure immediate shutdown. 1753 deferMessage(msg); 1754 1755 transitionTo(mDisconnectingState); 1756 break; 1757 case EVENT_SETUP_COMPLETED: // fallthrough 1758 case EVENT_IKE_CONNECTION_INFO_CHANGED: // fallthrough 1759 case EVENT_TRANSFORM_CREATED: 1760 // Child setup complete; move to ConnectedState for NetworkAgent registration 1761 deferMessage(msg); 1762 transitionTo(mConnectedState); 1763 break; 1764 case EVENT_DISCONNECT_REQUESTED: 1765 handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj); 1766 break; 1767 case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED: 1768 handleSafeModeTimeoutExceeded(); 1769 break; 1770 default: 1771 logUnhandledMessage(msg); 1772 break; 1773 } 1774 } 1775 } 1776 1777 private abstract class ConnectedStateBase extends ActiveBaseState { updateNetworkAgent( @onNull IpSecTunnelInterface tunnelIface, @NonNull VcnNetworkAgent agent, @NonNull VcnChildSessionConfiguration childConfig, @NonNull IkeSessionConnectionInfo ikeConnectionInfo)1778 protected void updateNetworkAgent( 1779 @NonNull IpSecTunnelInterface tunnelIface, 1780 @NonNull VcnNetworkAgent agent, 1781 @NonNull VcnChildSessionConfiguration childConfig, 1782 @NonNull IkeSessionConnectionInfo ikeConnectionInfo) { 1783 final NetworkCapabilities caps = 1784 buildNetworkCapabilities(mConnectionConfig, mUnderlying, mIsMobileDataEnabled); 1785 final LinkProperties lp = 1786 buildConnectedLinkProperties( 1787 mConnectionConfig, 1788 tunnelIface, 1789 childConfig, 1790 mUnderlying, 1791 ikeConnectionInfo); 1792 1793 agent.sendNetworkCapabilities(caps); 1794 agent.sendLinkProperties(lp); 1795 1796 agent.setUnderlyingNetworks( 1797 mUnderlying == null ? null : Collections.singletonList(mUnderlying.network)); 1798 } 1799 buildNetworkAgent( @onNull IpSecTunnelInterface tunnelIface, @NonNull VcnChildSessionConfiguration childConfig, @NonNull IkeSessionConnectionInfo ikeConnectionInfo)1800 protected VcnNetworkAgent buildNetworkAgent( 1801 @NonNull IpSecTunnelInterface tunnelIface, 1802 @NonNull VcnChildSessionConfiguration childConfig, 1803 @NonNull IkeSessionConnectionInfo ikeConnectionInfo) { 1804 final NetworkCapabilities caps = 1805 buildNetworkCapabilities(mConnectionConfig, mUnderlying, mIsMobileDataEnabled); 1806 final LinkProperties lp = 1807 buildConnectedLinkProperties( 1808 mConnectionConfig, 1809 tunnelIface, 1810 childConfig, 1811 mUnderlying, 1812 ikeConnectionInfo); 1813 final NetworkAgentConfig nac = 1814 new NetworkAgentConfig.Builder() 1815 .setLegacyType(ConnectivityManager.TYPE_MOBILE) 1816 .setLegacyTypeName(NETWORK_INFO_NETWORK_TYPE_STRING) 1817 .setLegacySubType(TelephonyManager.NETWORK_TYPE_UNKNOWN) 1818 .setLegacySubTypeName( 1819 TelephonyManager.getNetworkTypeName( 1820 TelephonyManager.NETWORK_TYPE_UNKNOWN)) 1821 .setLegacyExtraInfo(NETWORK_INFO_EXTRA_INFO) 1822 .build(); 1823 1824 final VcnNetworkAgent agent = 1825 mDeps.newNetworkAgent( 1826 mVcnContext, 1827 TAG, 1828 caps, 1829 lp, 1830 Vcn.getNetworkScore(), 1831 nac, 1832 mVcnContext.getVcnNetworkProvider(), 1833 (agentRef) -> { 1834 // Only trigger teardown if the NetworkAgent hasn't been replaced or 1835 // changed. This guards against two cases - the first where 1836 // unwanted() may be called as a result of the 1837 // NetworkAgent.unregister() call, which might trigger a teardown 1838 // instead of just a Network disconnect, as well as the case where a 1839 // new NetworkAgent replaces an old one before the unwanted() call 1840 // is processed. 1841 if (mNetworkAgent != agentRef) { 1842 logDbg("unwanted() called on stale NetworkAgent"); 1843 return; 1844 } 1845 1846 logInfo("NetworkAgent was unwanted"); 1847 teardownAsynchronously(); 1848 } /* networkUnwantedCallback */, 1849 (status) -> { 1850 if (mIsQuitting.getValue()) { 1851 return; // Ignore; VcnGatewayConnection quitting or already quit 1852 } 1853 1854 switch (status) { 1855 case NetworkAgent.VALIDATION_STATUS_VALID: 1856 clearFailedAttemptCounterAndSafeModeAlarm(); 1857 break; 1858 case NetworkAgent.VALIDATION_STATUS_NOT_VALID: 1859 // Trigger re-validation of underlying networks; if it 1860 // fails, the VCN will attempt to migrate away. 1861 if (mUnderlying != null) { 1862 mConnectivityManager.reportNetworkConnectivity( 1863 mUnderlying.network, 1864 false /* hasConnectivity */); 1865 } 1866 1867 // Will only set a new alarm if no safe mode alarm is 1868 // currently scheduled. 1869 setSafeModeAlarm(); 1870 break; 1871 default: 1872 logWtf( 1873 "Unknown validation status " 1874 + status 1875 + "; ignoring"); 1876 break; 1877 } 1878 } /* validationStatusCallback */); 1879 1880 agent.register(); 1881 agent.markConnected(); 1882 1883 return agent; 1884 } 1885 clearFailedAttemptCounterAndSafeModeAlarm()1886 protected void clearFailedAttemptCounterAndSafeModeAlarm() { 1887 mVcnContext.ensureRunningOnLooperThread(); 1888 1889 // Validated connection, clear failed attempt counter 1890 mFailedAttempts = 0; 1891 cancelSafeModeAlarm(); 1892 1893 mIsInSafeMode = false; 1894 mGatewayStatusCallback.onSafeModeStatusChanged(); 1895 } 1896 applyTransform( int token, @NonNull IpSecTunnelInterface tunnelIface, @NonNull Network underlyingNetwork, @NonNull IpSecTransform transform, int direction)1897 protected void applyTransform( 1898 int token, 1899 @NonNull IpSecTunnelInterface tunnelIface, 1900 @NonNull Network underlyingNetwork, 1901 @NonNull IpSecTransform transform, 1902 int direction) { 1903 if (direction != IpSecManager.DIRECTION_IN && direction != IpSecManager.DIRECTION_OUT) { 1904 logWtf("Applying transform for unexpected direction: " + direction); 1905 } 1906 1907 try { 1908 tunnelIface.setUnderlyingNetwork(underlyingNetwork); 1909 1910 // Transforms do not need to be persisted; the IkeSession will keep them alive 1911 mIpSecManager.applyTunnelModeTransform(tunnelIface, direction, transform); 1912 1913 if (direction == IpSecManager.DIRECTION_IN 1914 && mVcnContext.isFlagNetworkMetricMonitorEnabled() 1915 && mVcnContext.isFlagIpSecTransformStateEnabled()) { 1916 mUnderlyingNetworkController.updateInboundTransform(mUnderlying, transform); 1917 } 1918 1919 // For inbound transforms, additionally allow forwarded traffic to bridge to DUN (as 1920 // needed) 1921 final Set<Integer> exposedCaps = mConnectionConfig.getAllExposedCapabilities(); 1922 if (direction == IpSecManager.DIRECTION_IN 1923 && exposedCaps.contains(NET_CAPABILITY_DUN)) { 1924 mIpSecManager.applyTunnelModeTransform( 1925 tunnelIface, IpSecManager.DIRECTION_FWD, transform); 1926 } 1927 } catch (IOException | IllegalArgumentException e) { 1928 logInfo("Transform application failed for network " + token, e); 1929 sessionLost(token, e); 1930 } 1931 } 1932 setupInterface( int token, @NonNull IpSecTunnelInterface tunnelIface, @NonNull VcnChildSessionConfiguration childConfig, @Nullable VcnChildSessionConfiguration oldChildConfig)1933 protected void setupInterface( 1934 int token, 1935 @NonNull IpSecTunnelInterface tunnelIface, 1936 @NonNull VcnChildSessionConfiguration childConfig, 1937 @Nullable VcnChildSessionConfiguration oldChildConfig) { 1938 try { 1939 final Set<LinkAddress> newAddrs = 1940 new ArraySet<>(childConfig.getInternalAddresses()); 1941 final Set<LinkAddress> existingAddrs = new ArraySet<>(); 1942 if (oldChildConfig != null) { 1943 existingAddrs.addAll(oldChildConfig.getInternalAddresses()); 1944 } 1945 1946 final Set<LinkAddress> toAdd = new ArraySet<>(); 1947 toAdd.addAll(newAddrs); 1948 toAdd.removeAll(existingAddrs); 1949 1950 final Set<LinkAddress> toRemove = new ArraySet<>(); 1951 toRemove.addAll(existingAddrs); 1952 toRemove.removeAll(newAddrs); 1953 1954 for (LinkAddress address : toAdd) { 1955 tunnelIface.addAddress(address.getAddress(), address.getPrefixLength()); 1956 } 1957 1958 for (LinkAddress address : toRemove) { 1959 tunnelIface.removeAddress(address.getAddress(), address.getPrefixLength()); 1960 } 1961 } catch (IOException e) { 1962 logInfo("Adding address to tunnel failed for token " + token, e); 1963 sessionLost(token, e); 1964 } 1965 } 1966 } 1967 1968 /** 1969 * Stable state representing a VCN that has a functioning connection to the mobility anchor. 1970 * 1971 * <p>This state handles IPsec transform application (initial and rekey), NetworkAgent setup, 1972 * and monitors for mobility events. 1973 */ 1974 class ConnectedState extends ConnectedStateBase { 1975 @Override enterState()1976 protected void enterState() throws Exception { 1977 if (mTunnelIface == null) { 1978 try { 1979 // Requires a real Network object in order to be created; doing this any earlier 1980 // means not having a real Network object, or picking an incorrect Network. 1981 mTunnelIface = 1982 mIpSecManager.createIpSecTunnelInterface( 1983 DUMMY_ADDR, DUMMY_ADDR, mUnderlying.network); 1984 } catch (IOException | ResourceUnavailableException e) { 1985 teardownAsynchronously(); 1986 } 1987 } 1988 } 1989 1990 @Override processStateMsg(Message msg)1991 protected void processStateMsg(Message msg) { 1992 switch (msg.what) { 1993 case EVENT_UNDERLYING_NETWORK_CHANGED: 1994 handleUnderlyingNetworkChanged(msg); 1995 break; 1996 case EVENT_SESSION_CLOSED: 1997 // Disconnecting state waits for EVENT_SESSION_CLOSED to shutdown, and this 1998 // message may not be posted again. Defer to ensure immediate shutdown. 1999 deferMessage(msg); 2000 transitionTo(mDisconnectingState); 2001 break; 2002 case EVENT_SESSION_LOST: 2003 transitionTo(mDisconnectingState); 2004 break; 2005 case EVENT_TRANSFORM_CREATED: 2006 final EventTransformCreatedInfo transformCreatedInfo = 2007 (EventTransformCreatedInfo) msg.obj; 2008 2009 applyTransform( 2010 mCurrentToken, 2011 mTunnelIface, 2012 mUnderlying.network, 2013 transformCreatedInfo.transform, 2014 transformCreatedInfo.direction); 2015 break; 2016 case EVENT_SETUP_COMPLETED: 2017 final VcnChildSessionConfiguration oldChildConfig = mChildConfig; 2018 mChildConfig = ((EventSetupCompletedInfo) msg.obj).childSessionConfig; 2019 2020 setupInterfaceAndNetworkAgent( 2021 mCurrentToken, 2022 mTunnelIface, 2023 mChildConfig, 2024 oldChildConfig, 2025 mIkeConnectionInfo); 2026 2027 // Create opportunistic child SAs; this allows SA aggregation in the downlink, 2028 // reducing lock/atomic contention in high throughput scenarios. All SAs will 2029 // share the same UDP encap socket (and keepalives) as necessary, and are 2030 // effectively free. 2031 final int parallelTunnelCount = 2032 mDeps.getParallelTunnelCount(mLastSnapshot, mSubscriptionGroup); 2033 logInfo("Parallel tunnel count: " + parallelTunnelCount); 2034 2035 for (int i = 0; i < parallelTunnelCount - 1; i++) { 2036 mIkeSession.openChildSession( 2037 buildOpportunisticChildParams(), 2038 new VcnChildSessionCallback( 2039 mCurrentToken, true /* isOpportunistic */)); 2040 } 2041 2042 break; 2043 case EVENT_DISCONNECT_REQUESTED: 2044 handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj); 2045 break; 2046 case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED: 2047 handleSafeModeTimeoutExceeded(); 2048 break; 2049 case EVENT_MIGRATION_COMPLETED: 2050 final EventMigrationCompletedInfo migrationCompletedInfo = 2051 (EventMigrationCompletedInfo) msg.obj; 2052 2053 handleMigrationCompleted(migrationCompletedInfo); 2054 break; 2055 case EVENT_IKE_CONNECTION_INFO_CHANGED: 2056 mIkeConnectionInfo = 2057 ((EventIkeConnectionInfoChangedInfo) msg.obj).ikeConnectionInfo; 2058 break; 2059 case EVENT_DATA_STALL_SUSPECTED: 2060 final Network networkWithDataStall = 2061 ((EventDataStallSuspectedInfo) msg.obj).network; 2062 handleDataStallSuspected(networkWithDataStall); 2063 break; 2064 default: 2065 logUnhandledMessage(msg); 2066 break; 2067 } 2068 } 2069 handleMigrationCompleted(EventMigrationCompletedInfo migrationCompletedInfo)2070 private void handleMigrationCompleted(EventMigrationCompletedInfo migrationCompletedInfo) { 2071 logInfo("Migration completed: " + mUnderlying.network); 2072 2073 applyTransform( 2074 mCurrentToken, 2075 mTunnelIface, 2076 mUnderlying.network, 2077 migrationCompletedInfo.inTransform, 2078 IpSecManager.DIRECTION_IN); 2079 2080 applyTransform( 2081 mCurrentToken, 2082 mTunnelIface, 2083 mUnderlying.network, 2084 migrationCompletedInfo.outTransform, 2085 IpSecManager.DIRECTION_OUT); 2086 2087 updateNetworkAgent(mTunnelIface, mNetworkAgent, mChildConfig, mIkeConnectionInfo); 2088 2089 // Trigger re-validation after migration events. 2090 mConnectivityManager.reportNetworkConnectivity( 2091 mNetworkAgent.getNetwork(), false /* hasConnectivity */); 2092 } 2093 handleUnderlyingNetworkChanged(@onNull Message msg)2094 private void handleUnderlyingNetworkChanged(@NonNull Message msg) { 2095 final UnderlyingNetworkRecord oldUnderlying = mUnderlying; 2096 mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying; 2097 2098 if (mUnderlying == null) { 2099 logInfo("Underlying network lost"); 2100 2101 // Ignored for now; a new network may be coming up. If none does, the delayed 2102 // NETWORK_LOST disconnect will be fired, and tear down the session + network. 2103 return; 2104 } 2105 2106 // mUnderlying assumed non-null, given check above. 2107 // If network changed, migrate. Otherwise, update any existing networkAgent. 2108 if (oldUnderlying == null || !oldUnderlying.network.equals(mUnderlying.network)) { 2109 logInfo("Migrating to new network: " + mUnderlying.network); 2110 mIkeSession.setNetwork(mUnderlying.network); 2111 } else { 2112 // oldUnderlying is non-null & underlying network itself has not changed 2113 // (only network properties were changed). 2114 2115 // Network not yet set up, or child not yet connected. 2116 if (mNetworkAgent != null && mChildConfig != null) { 2117 // If only network properties changed and agent is active, update properties 2118 updateNetworkAgent( 2119 mTunnelIface, mNetworkAgent, mChildConfig, mIkeConnectionInfo); 2120 } 2121 } 2122 } 2123 handleDataStallSuspected(Network networkWithDataStall)2124 private void handleDataStallSuspected(Network networkWithDataStall) { 2125 if (mUnderlying != null 2126 && mNetworkAgent != null 2127 && mNetworkAgent.getNetwork().equals(networkWithDataStall)) { 2128 logInfo("Perform Mobility update to recover from suspected data stall"); 2129 mIkeSession.setNetwork(mUnderlying.network); 2130 } 2131 } 2132 setupInterfaceAndNetworkAgent( int token, @NonNull IpSecTunnelInterface tunnelIface, @NonNull VcnChildSessionConfiguration childConfig, @NonNull VcnChildSessionConfiguration oldChildConfig, @NonNull IkeSessionConnectionInfo ikeConnectionInfo)2133 protected void setupInterfaceAndNetworkAgent( 2134 int token, 2135 @NonNull IpSecTunnelInterface tunnelIface, 2136 @NonNull VcnChildSessionConfiguration childConfig, 2137 @NonNull VcnChildSessionConfiguration oldChildConfig, 2138 @NonNull IkeSessionConnectionInfo ikeConnectionInfo) { 2139 setupInterface(token, tunnelIface, childConfig, oldChildConfig); 2140 2141 if (mNetworkAgent == null) { 2142 mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig, ikeConnectionInfo); 2143 } else { 2144 updateNetworkAgent(tunnelIface, mNetworkAgent, childConfig, ikeConnectionInfo); 2145 2146 // mNetworkAgent not null, so the VCN Network has already been established. Clear 2147 // the failed attempt counter and safe mode alarm since this transition is complete. 2148 clearFailedAttemptCounterAndSafeModeAlarm(); 2149 } 2150 } 2151 2152 @Override exitState()2153 protected void exitState() { 2154 // Will only set a new alarm if no safe mode alarm is currently scheduled. 2155 setSafeModeAlarm(); 2156 } 2157 } 2158 2159 /** 2160 * Transitive state representing a VCN that failed to establish a connection, and will retry. 2161 * 2162 * <p>This state will be exited upon a new underlying network being found, or timeout expiry. 2163 */ 2164 class RetryTimeoutState extends ActiveBaseState { 2165 @Override enterState()2166 protected void enterState() throws Exception { 2167 // Reset upon entry to ConnectedState 2168 mFailedAttempts++; 2169 2170 if (mUnderlying == null) { 2171 logWtf("Underlying network was null in retry state"); 2172 teardownNetwork(); 2173 transitionTo(mDisconnectedState); 2174 } else { 2175 // Safe to blindly set up, as it is cancelled and cleared on exiting this state 2176 setRetryTimeoutAlarm(getNextRetryIntervalsMs()); 2177 } 2178 } 2179 2180 @Override processStateMsg(Message msg)2181 protected void processStateMsg(Message msg) { 2182 switch (msg.what) { 2183 case EVENT_UNDERLYING_NETWORK_CHANGED: 2184 final UnderlyingNetworkRecord oldUnderlying = mUnderlying; 2185 mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying; 2186 2187 // If new underlying is null, all networks were lost; go back to disconnected. 2188 if (mUnderlying == null) { 2189 teardownNetwork(); 2190 transitionTo(mDisconnectedState); 2191 return; 2192 } else if (oldUnderlying != null 2193 && mUnderlying.network.equals(oldUnderlying.network)) { 2194 // If the network has not changed, do nothing. 2195 return; 2196 } 2197 2198 // Fallthrough 2199 case EVENT_RETRY_TIMEOUT_EXPIRED: 2200 transitionTo(mConnectingState); 2201 break; 2202 case EVENT_DISCONNECT_REQUESTED: 2203 handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj); 2204 break; 2205 case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED: 2206 handleSafeModeTimeoutExceeded(); 2207 break; 2208 default: 2209 logUnhandledMessage(msg); 2210 break; 2211 } 2212 } 2213 2214 @Override exitState()2215 public void exitState() { 2216 cancelRetryTimeoutAlarm(); 2217 } 2218 getNextRetryIntervalsMs()2219 private long getNextRetryIntervalsMs() { 2220 final int retryDelayIndex = mFailedAttempts - 1; 2221 final long[] retryIntervalsMs = mConnectionConfig.getRetryIntervalsMillis(); 2222 2223 // Repeatedly use last item in retry timeout list. 2224 if (retryDelayIndex >= retryIntervalsMs.length) { 2225 return retryIntervalsMs[retryIntervalsMs.length - 1]; 2226 } 2227 2228 return retryIntervalsMs[retryDelayIndex]; 2229 } 2230 } 2231 2232 @VisibleForTesting(visibility = Visibility.PRIVATE) buildNetworkCapabilities( @onNull VcnGatewayConnectionConfig gatewayConnectionConfig, @Nullable UnderlyingNetworkRecord underlying, boolean isMobileDataEnabled)2233 static NetworkCapabilities buildNetworkCapabilities( 2234 @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig, 2235 @Nullable UnderlyingNetworkRecord underlying, 2236 boolean isMobileDataEnabled) { 2237 final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(); 2238 2239 builder.addTransportType(TRANSPORT_CELLULAR); 2240 builder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); 2241 builder.addCapability(NET_CAPABILITY_NOT_CONGESTED); 2242 builder.addCapability(NET_CAPABILITY_NOT_SUSPENDED); 2243 2244 // Add exposed capabilities 2245 for (int cap : gatewayConnectionConfig.getAllExposedCapabilities()) { 2246 // Skip adding INTERNET or DUN if mobile data is disabled. 2247 if (!isMobileDataEnabled 2248 && (cap == NET_CAPABILITY_INTERNET || cap == NET_CAPABILITY_DUN)) { 2249 continue; 2250 } 2251 2252 builder.addCapability(cap); 2253 } 2254 2255 if (underlying != null) { 2256 final NetworkCapabilities underlyingCaps = underlying.networkCapabilities; 2257 2258 // Mirror merged capabilities. 2259 for (int cap : MERGED_CAPABILITIES) { 2260 if (underlyingCaps.hasCapability(cap)) { 2261 builder.addCapability(cap); 2262 } 2263 } 2264 2265 // Set admin UIDs for ConnectivityDiagnostics use. 2266 final int[] underlyingAdminUids = underlyingCaps.getAdministratorUids(); 2267 Arrays.sort(underlyingAdminUids); // Sort to allow contains check below. 2268 2269 int[] adminUids; 2270 if (underlyingCaps.getOwnerUid() > 0 // No owner UID specified 2271 && 0 > Arrays.binarySearch(// Owner UID not found in admin UID list. 2272 underlyingAdminUids, underlyingCaps.getOwnerUid())) { 2273 adminUids = Arrays.copyOf(underlyingAdminUids, underlyingAdminUids.length + 1); 2274 adminUids[adminUids.length - 1] = underlyingCaps.getOwnerUid(); 2275 Arrays.sort(adminUids); 2276 } else { 2277 adminUids = underlyingAdminUids; 2278 } 2279 2280 // Set owner & administrator UID 2281 builder.setOwnerUid(Process.myUid()); 2282 adminUids = Arrays.copyOf(adminUids, adminUids.length + 1); 2283 adminUids[adminUids.length - 1] = Process.myUid(); 2284 builder.setAdministratorUids(adminUids); 2285 2286 builder.setLinkUpstreamBandwidthKbps(underlyingCaps.getLinkUpstreamBandwidthKbps()); 2287 builder.setLinkDownstreamBandwidthKbps(underlyingCaps.getLinkDownstreamBandwidthKbps()); 2288 2289 // Set TransportInfo for SysUI use (never parcelled out of SystemServer). 2290 if (underlyingCaps.hasTransport(TRANSPORT_WIFI) 2291 && underlyingCaps.getTransportInfo() instanceof WifiInfo) { 2292 final WifiInfo wifiInfo = (WifiInfo) underlyingCaps.getTransportInfo(); 2293 builder.setTransportInfo( 2294 new VcnTransportInfo( 2295 wifiInfo, 2296 gatewayConnectionConfig.getMinUdpPort4500NatTimeoutSeconds())); 2297 } else if (underlyingCaps.hasTransport(TRANSPORT_CELLULAR) 2298 && underlyingCaps.getNetworkSpecifier() instanceof TelephonyNetworkSpecifier) { 2299 final TelephonyNetworkSpecifier telNetSpecifier = 2300 (TelephonyNetworkSpecifier) underlyingCaps.getNetworkSpecifier(); 2301 builder.setTransportInfo( 2302 new VcnTransportInfo( 2303 telNetSpecifier.getSubscriptionId(), 2304 gatewayConnectionConfig.getMinUdpPort4500NatTimeoutSeconds())); 2305 } else { 2306 Slog.wtf( 2307 TAG, 2308 "Unknown transport type or missing TransportInfo/NetworkSpecifier for" 2309 + " non-null underlying network"); 2310 } 2311 builder.setUnderlyingNetworks(List.of(underlying.network)); 2312 } else { 2313 Slog.wtf( 2314 TAG, 2315 "No underlying network while building network capabilities", 2316 new IllegalStateException()); 2317 } 2318 2319 return builder.build(); 2320 } 2321 2322 @VisibleForTesting(visibility = Visibility.PRIVATE) buildConnectedLinkProperties( @onNull VcnGatewayConnectionConfig gatewayConnectionConfig, @NonNull IpSecTunnelInterface tunnelIface, @NonNull VcnChildSessionConfiguration childConfig, @Nullable UnderlyingNetworkRecord underlying, @NonNull IkeSessionConnectionInfo ikeConnectionInfo)2323 LinkProperties buildConnectedLinkProperties( 2324 @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig, 2325 @NonNull IpSecTunnelInterface tunnelIface, 2326 @NonNull VcnChildSessionConfiguration childConfig, 2327 @Nullable UnderlyingNetworkRecord underlying, 2328 @NonNull IkeSessionConnectionInfo ikeConnectionInfo) { 2329 final IkeTunnelConnectionParams ikeTunnelParams = 2330 gatewayConnectionConfig.getTunnelConnectionParams(); 2331 final LinkProperties lp = new LinkProperties(); 2332 2333 lp.setInterfaceName(tunnelIface.getInterfaceName()); 2334 for (LinkAddress addr : childConfig.getInternalAddresses()) { 2335 lp.addLinkAddress(addr); 2336 } 2337 for (InetAddress addr : childConfig.getInternalDnsServers()) { 2338 lp.addDnsServer(addr); 2339 } 2340 2341 lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null /*gateway*/, 2342 null /*iface*/, RouteInfo.RTN_UNICAST)); 2343 lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null /*gateway*/, 2344 null /*iface*/, RouteInfo.RTN_UNICAST)); 2345 2346 int underlyingMtu = 0; 2347 if (underlying != null) { 2348 final LinkProperties underlyingLp = underlying.linkProperties; 2349 2350 lp.setTcpBufferSizes(underlyingLp.getTcpBufferSizes()); 2351 underlyingMtu = underlyingLp.getMtu(); 2352 2353 // WiFi LinkProperties uses DHCP as the sole source of MTU information, and as a result 2354 // often lists MTU as 0 (see b/184678973). Use the interface MTU as retrieved by 2355 // NetworkInterface APIs. 2356 if (underlyingMtu == 0 && underlyingLp.getInterfaceName() != null) { 2357 underlyingMtu = mDeps.getUnderlyingIfaceMtu(underlyingLp.getInterfaceName()); 2358 } 2359 } else { 2360 Slog.wtf( 2361 TAG, 2362 "No underlying network while building link properties", 2363 new IllegalStateException()); 2364 } 2365 lp.setMtu( 2366 MtuUtils.getMtu( 2367 ikeTunnelParams.getTunnelModeChildSessionParams().getSaProposals(), 2368 gatewayConnectionConfig.getMaxMtu(), 2369 underlyingMtu, 2370 ikeConnectionInfo.getLocalAddress() instanceof Inet4Address)); 2371 2372 return lp; 2373 } 2374 2375 private class IkeSessionCallbackImpl implements IkeSessionCallback { 2376 private final int mToken; 2377 IkeSessionCallbackImpl(int token)2378 IkeSessionCallbackImpl(int token) { 2379 mToken = token; 2380 } 2381 2382 @Override onOpened(@onNull IkeSessionConfiguration ikeSessionConfig)2383 public void onOpened(@NonNull IkeSessionConfiguration ikeSessionConfig) { 2384 logDbg("IkeOpened for token " + mToken); 2385 ikeConnectionInfoChanged(mToken, ikeSessionConfig.getIkeSessionConnectionInfo()); 2386 } 2387 2388 @Override onClosed()2389 public void onClosed() { 2390 logDbg("IkeClosed for token " + mToken); 2391 sessionClosed(mToken, null); 2392 } 2393 2394 @Override onClosedExceptionally(@onNull IkeException exception)2395 public void onClosedExceptionally(@NonNull IkeException exception) { 2396 logInfo("IkeClosedExceptionally for token " + mToken, exception); 2397 sessionClosed(mToken, exception); 2398 } 2399 2400 @Override onError(@onNull IkeProtocolException exception)2401 public void onError(@NonNull IkeProtocolException exception) { 2402 logInfo("IkeError for token " + mToken, exception); 2403 // Non-fatal, log and continue. 2404 } 2405 2406 @Override onIkeSessionConnectionInfoChanged( @onNull IkeSessionConnectionInfo connectionInfo)2407 public void onIkeSessionConnectionInfoChanged( 2408 @NonNull IkeSessionConnectionInfo connectionInfo) { 2409 logDbg("onIkeSessionConnectionInfoChanged for token " + mToken); 2410 ikeConnectionInfoChanged(mToken, connectionInfo); 2411 } 2412 } 2413 2414 /** Implementation of ChildSessionCallback, exposed for testing. */ 2415 @VisibleForTesting(visibility = Visibility.PRIVATE) 2416 public class VcnChildSessionCallback implements ChildSessionCallback { 2417 private final int mToken; 2418 private final boolean mIsOpportunistic; 2419 2420 private boolean mIsChildOpened = false; 2421 VcnChildSessionCallback(int token)2422 VcnChildSessionCallback(int token) { 2423 this(token, false /* isOpportunistic */); 2424 } 2425 2426 /** 2427 * Creates a ChildSessionCallback 2428 * 2429 * <p>If configured as opportunistic, transforms will not report initial startup, or 2430 * associated startup failures. This serves the dual purposes of ensuring that if the server 2431 * does not support connection multiplexing, new child SA negotiations will be ignored, and 2432 * at the same time, will notify the VCN session if a successfully negotiated opportunistic 2433 * child SA is subsequently torn down, which could impact uplink traffic if the SA in use 2434 * for outbound/uplink traffic is this opportunistic SA. 2435 * 2436 * <p>While inbound SAs can be used in parallel, the IPsec stack explicitly selects the last 2437 * applied outbound transform for outbound traffic. This means that unlike inbound traffic, 2438 * outbound does not benefit from these parallel SAs in the same manner. 2439 */ VcnChildSessionCallback(int token, boolean isOpportunistic)2440 VcnChildSessionCallback(int token, boolean isOpportunistic) { 2441 mToken = token; 2442 mIsOpportunistic = isOpportunistic; 2443 } 2444 2445 /** Internal proxy method for injecting of mocked ChildSessionConfiguration */ 2446 @VisibleForTesting(visibility = Visibility.PRIVATE) onOpened(@onNull VcnChildSessionConfiguration childConfig)2447 void onOpened(@NonNull VcnChildSessionConfiguration childConfig) { 2448 logDbg("ChildOpened for token " + mToken); 2449 2450 if (mIsOpportunistic) { 2451 logDbg("ChildOpened for opportunistic child; suppressing event message"); 2452 mIsChildOpened = true; 2453 return; 2454 } 2455 2456 childOpened(mToken, childConfig); 2457 } 2458 2459 @Override onOpened(@onNull ChildSessionConfiguration childConfig)2460 public void onOpened(@NonNull ChildSessionConfiguration childConfig) { 2461 onOpened(new VcnChildSessionConfiguration(childConfig)); 2462 } 2463 2464 @Override onClosed()2465 public void onClosed() { 2466 logDbg("ChildClosed for token " + mToken); 2467 2468 if (mIsOpportunistic && !mIsChildOpened) { 2469 logDbg("ChildClosed for unopened opportunistic child; ignoring"); 2470 return; 2471 } 2472 2473 sessionLost(mToken, null); 2474 } 2475 2476 @Override onClosedExceptionally(@onNull IkeException exception)2477 public void onClosedExceptionally(@NonNull IkeException exception) { 2478 logInfo("ChildClosedExceptionally for token " + mToken, exception); 2479 2480 if (mIsOpportunistic && !mIsChildOpened) { 2481 logInfo("ChildClosedExceptionally for unopened opportunistic child; ignoring"); 2482 return; 2483 } 2484 2485 sessionLost(mToken, exception); 2486 } 2487 2488 @Override onIpSecTransformCreated(@onNull IpSecTransform transform, int direction)2489 public void onIpSecTransformCreated(@NonNull IpSecTransform transform, int direction) { 2490 logDbg("ChildTransformCreated; Direction: " + direction + "; token " + mToken); 2491 childTransformCreated(mToken, transform, direction); 2492 } 2493 2494 @Override onIpSecTransformsMigrated( @onNull IpSecTransform inIpSecTransform, @NonNull IpSecTransform outIpSecTransform)2495 public void onIpSecTransformsMigrated( 2496 @NonNull IpSecTransform inIpSecTransform, 2497 @NonNull IpSecTransform outIpSecTransform) { 2498 logDbg("ChildTransformsMigrated; token " + mToken); 2499 migrationCompleted(mToken, inIpSecTransform, outIpSecTransform); 2500 } 2501 2502 @Override onIpSecTransformDeleted(@onNull IpSecTransform transform, int direction)2503 public void onIpSecTransformDeleted(@NonNull IpSecTransform transform, int direction) { 2504 // Nothing to be done; no references to the IpSecTransform are held, and this transform 2505 // will be closed by the IKE library. 2506 logDbg("ChildTransformDeleted; Direction: " + direction + "; for token " + mToken); 2507 } 2508 } 2509 2510 // Used in Vcn.java, but must be public for mockito to mock this. getLogPrefix()2511 public String getLogPrefix() { 2512 return "(" 2513 + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup) 2514 + "-" 2515 + mConnectionConfig.getGatewayConnectionName() 2516 + "-" 2517 + System.identityHashCode(this) 2518 + ") "; 2519 } 2520 getTagLogPrefix()2521 private String getTagLogPrefix() { 2522 return "[ " + TAG + " " + getLogPrefix() + "]"; 2523 } 2524 logVdbg(String msg)2525 private void logVdbg(String msg) { 2526 if (VDBG) { 2527 Slog.v(TAG, getLogPrefix() + msg); 2528 } 2529 } 2530 logDbg(String msg)2531 private void logDbg(String msg) { 2532 Slog.d(TAG, getLogPrefix() + msg); 2533 } 2534 logDbg(String msg, Throwable tr)2535 private void logDbg(String msg, Throwable tr) { 2536 Slog.d(TAG, getLogPrefix() + msg, tr); 2537 } 2538 logInfo(String msg)2539 private void logInfo(String msg) { 2540 Slog.i(TAG, getLogPrefix() + msg); 2541 LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg); 2542 } 2543 logInfo(String msg, Throwable tr)2544 private void logInfo(String msg, Throwable tr) { 2545 Slog.i(TAG, getLogPrefix() + msg, tr); 2546 LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg + tr); 2547 } 2548 logWarn(String msg)2549 private void logWarn(String msg) { 2550 Slog.w(TAG, getLogPrefix() + msg); 2551 LOCAL_LOG.log("[WARN] " + getTagLogPrefix() + msg); 2552 } 2553 logWarn(String msg, Throwable tr)2554 private void logWarn(String msg, Throwable tr) { 2555 Slog.w(TAG, getLogPrefix() + msg, tr); 2556 LOCAL_LOG.log("[WARN] " + getTagLogPrefix() + msg + tr); 2557 } 2558 logErr(String msg)2559 private void logErr(String msg) { 2560 Slog.e(TAG, getLogPrefix() + msg); 2561 LOCAL_LOG.log("[ERR ] " + getTagLogPrefix() + msg); 2562 } 2563 logErr(String msg, Throwable tr)2564 private void logErr(String msg, Throwable tr) { 2565 Slog.e(TAG, getLogPrefix() + msg, tr); 2566 LOCAL_LOG.log("[ERR ] " + getTagLogPrefix() + msg + tr); 2567 } 2568 logWtf(String msg)2569 private void logWtf(String msg) { 2570 Slog.wtf(TAG, getLogPrefix() + msg); 2571 LOCAL_LOG.log("[WTF ] " + msg); 2572 } 2573 logWtf(String msg, Throwable tr)2574 private void logWtf(String msg, Throwable tr) { 2575 Slog.wtf(TAG, getLogPrefix() + msg, tr); 2576 LOCAL_LOG.log("[WTF ] " + msg + tr); 2577 } 2578 2579 /** 2580 * Dumps the state of this VcnGatewayConnection for logging and debugging purposes. 2581 * 2582 * <p>PII and credentials MUST NEVER be dumped here. 2583 * 2584 * <p>This method is not thread safe and MUST run on the VCN thread. 2585 */ dump(IndentingPrintWriter pw)2586 public void dump(IndentingPrintWriter pw) { 2587 mVcnContext.ensureRunningOnLooperThread(); 2588 2589 pw.println("VcnGatewayConnection (" + mConnectionConfig.getGatewayConnectionName() + "):"); 2590 pw.increaseIndent(); 2591 2592 pw.println( 2593 "Current state: " 2594 + (getCurrentState() == null 2595 ? null 2596 : getCurrentState().getClass().getSimpleName())); 2597 pw.println("mIsQuitting: " + mIsQuitting.getValue()); 2598 pw.println("mIsInSafeMode: " + mIsInSafeMode); 2599 pw.println("mCurrentToken: " + mCurrentToken); 2600 pw.println("mFailedAttempts: " + mFailedAttempts); 2601 pw.println( 2602 "mNetworkAgent.getNetwork(): " 2603 + (mNetworkAgent == null ? null : mNetworkAgent.getNetwork())); 2604 pw.println(); 2605 2606 mUnderlyingNetworkController.dump(pw); 2607 pw.println(); 2608 2609 if (mIkeSession == null) { 2610 pw.println("mIkeSession: null"); 2611 } else { 2612 pw.println("mIkeSession:"); 2613 2614 // Add a try catch block in case IkeSession#dump is not thread-safe 2615 try { 2616 mIkeSession.dump(pw); 2617 } catch (Exception e) { 2618 Slog.wtf(TAG, "Failed to dump IkeSession: " + e); 2619 } 2620 } 2621 2622 pw.decreaseIndent(); 2623 } 2624 2625 @VisibleForTesting(visibility = Visibility.PRIVATE) setTunnelInterface(IpSecTunnelInterface tunnelIface)2626 void setTunnelInterface(IpSecTunnelInterface tunnelIface) { 2627 mTunnelIface = tunnelIface; 2628 } 2629 2630 @VisibleForTesting(visibility = Visibility.PRIVATE) getUnderlyingNetworkControllerCallback()2631 UnderlyingNetworkControllerCallback getUnderlyingNetworkControllerCallback() { 2632 return mUnderlyingNetworkControllerCallback; 2633 } 2634 2635 @VisibleForTesting(visibility = Visibility.PRIVATE) getConnectivityDiagnosticsCallback()2636 ConnectivityDiagnosticsCallback getConnectivityDiagnosticsCallback() { 2637 return mConnectivityDiagnosticsCallback; 2638 } 2639 2640 @VisibleForTesting(visibility = Visibility.PRIVATE) getUnderlyingNetwork()2641 UnderlyingNetworkRecord getUnderlyingNetwork() { 2642 return mUnderlying; 2643 } 2644 2645 @VisibleForTesting(visibility = Visibility.PRIVATE) setUnderlyingNetwork(@ullable UnderlyingNetworkRecord record)2646 void setUnderlyingNetwork(@Nullable UnderlyingNetworkRecord record) { 2647 mUnderlying = record; 2648 } 2649 2650 @VisibleForTesting(visibility = Visibility.PRIVATE) getIkeConnectionInfo()2651 IkeSessionConnectionInfo getIkeConnectionInfo() { 2652 return mIkeConnectionInfo; 2653 } 2654 2655 @VisibleForTesting(visibility = Visibility.PRIVATE) isQuitting()2656 boolean isQuitting() { 2657 return mIsQuitting.getValue(); 2658 } 2659 2660 @VisibleForTesting(visibility = Visibility.PRIVATE) setQuitting()2661 void setQuitting() { 2662 mIsQuitting.setTrue(); 2663 } 2664 2665 @VisibleForTesting(visibility = Visibility.PRIVATE) getIkeSession()2666 VcnIkeSession getIkeSession() { 2667 return mIkeSession; 2668 } 2669 2670 @VisibleForTesting(visibility = Visibility.PRIVATE) setIkeSession(@ullable VcnIkeSession session)2671 void setIkeSession(@Nullable VcnIkeSession session) { 2672 mIkeSession = session; 2673 } 2674 2675 @VisibleForTesting(visibility = Visibility.PRIVATE) getNetworkAgent()2676 VcnNetworkAgent getNetworkAgent() { 2677 return mNetworkAgent; 2678 } 2679 2680 @VisibleForTesting(visibility = Visibility.PRIVATE) setNetworkAgent(@ullable VcnNetworkAgent networkAgent)2681 void setNetworkAgent(@Nullable VcnNetworkAgent networkAgent) { 2682 mNetworkAgent = networkAgent; 2683 } 2684 2685 @VisibleForTesting(visibility = Visibility.PRIVATE) sendDisconnectRequestedAndAcquireWakelock(String reason, boolean shouldQuit)2686 void sendDisconnectRequestedAndAcquireWakelock(String reason, boolean shouldQuit) { 2687 sendMessageAndAcquireWakeLock( 2688 EVENT_DISCONNECT_REQUESTED, 2689 TOKEN_ALL, 2690 new EventDisconnectRequestedInfo(reason, shouldQuit)); 2691 } 2692 buildIkeParams(@onNull Network network)2693 private IkeSessionParams buildIkeParams(@NonNull Network network) { 2694 final IkeTunnelConnectionParams ikeTunnelConnectionParams = 2695 mConnectionConfig.getTunnelConnectionParams(); 2696 final IkeSessionParams.Builder builder = 2697 new IkeSessionParams.Builder(ikeTunnelConnectionParams.getIkeSessionParams()); 2698 builder.setNetwork(network); 2699 return builder.build(); 2700 } 2701 buildChildParams()2702 private ChildSessionParams buildChildParams() { 2703 return mConnectionConfig.getTunnelConnectionParams().getTunnelModeChildSessionParams(); 2704 } 2705 buildOpportunisticChildParams()2706 private ChildSessionParams buildOpportunisticChildParams() { 2707 final ChildSessionParams baseParams = 2708 mConnectionConfig.getTunnelConnectionParams().getTunnelModeChildSessionParams(); 2709 2710 final TunnelModeChildSessionParams.Builder builder = 2711 new TunnelModeChildSessionParams.Builder(); 2712 for (ChildSaProposal proposal : baseParams.getChildSaProposals()) { 2713 builder.addChildSaProposal(proposal); 2714 } 2715 2716 for (IkeTrafficSelector inboundSelector : baseParams.getInboundTrafficSelectors()) { 2717 builder.addInboundTrafficSelectors(inboundSelector); 2718 } 2719 2720 for (IkeTrafficSelector outboundSelector : baseParams.getOutboundTrafficSelectors()) { 2721 builder.addOutboundTrafficSelectors(outboundSelector); 2722 } 2723 2724 builder.setLifetimeSeconds( 2725 baseParams.getHardLifetimeSeconds(), baseParams.getSoftLifetimeSeconds()); 2726 2727 return builder.build(); 2728 } 2729 2730 @VisibleForTesting(visibility = Visibility.PRIVATE) buildIkeSession(@onNull Network network)2731 VcnIkeSession buildIkeSession(@NonNull Network network) { 2732 final int token = ++mCurrentToken; 2733 2734 return mDeps.newIkeSession( 2735 mVcnContext, 2736 buildIkeParams(network), 2737 buildChildParams(), 2738 new IkeSessionCallbackImpl(token), 2739 new VcnChildSessionCallback(token)); 2740 } 2741 2742 /** External dependencies used by VcnGatewayConnection, for injection in tests */ 2743 @VisibleForTesting(visibility = Visibility.PRIVATE) 2744 public static class Dependencies { 2745 /** Builds a new UnderlyingNetworkController. */ newUnderlyingNetworkController( VcnContext vcnContext, VcnGatewayConnectionConfig connectionConfig, ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, UnderlyingNetworkControllerCallback callback)2746 public UnderlyingNetworkController newUnderlyingNetworkController( 2747 VcnContext vcnContext, 2748 VcnGatewayConnectionConfig connectionConfig, 2749 ParcelUuid subscriptionGroup, 2750 TelephonySubscriptionSnapshot snapshot, 2751 UnderlyingNetworkControllerCallback callback) { 2752 return new UnderlyingNetworkController( 2753 vcnContext, connectionConfig, subscriptionGroup, snapshot, callback); 2754 } 2755 2756 /** Builds a new IkeSession. */ newIkeSession( VcnContext vcnContext, IkeSessionParams ikeSessionParams, ChildSessionParams childSessionParams, IkeSessionCallback ikeSessionCallback, ChildSessionCallback childSessionCallback)2757 public VcnIkeSession newIkeSession( 2758 VcnContext vcnContext, 2759 IkeSessionParams ikeSessionParams, 2760 ChildSessionParams childSessionParams, 2761 IkeSessionCallback ikeSessionCallback, 2762 ChildSessionCallback childSessionCallback) { 2763 return new VcnIkeSession( 2764 vcnContext, 2765 ikeSessionParams, 2766 childSessionParams, 2767 ikeSessionCallback, 2768 childSessionCallback); 2769 } 2770 2771 /** Builds a new WakeLock. */ newWakeLock( @onNull Context context, int wakeLockFlag, @NonNull String wakeLockTag)2772 public VcnWakeLock newWakeLock( 2773 @NonNull Context context, int wakeLockFlag, @NonNull String wakeLockTag) { 2774 return new VcnWakeLock(context, wakeLockFlag, wakeLockTag); 2775 } 2776 2777 /** Builds a new WakeupMessage. */ newWakeupMessage( @onNull VcnContext vcnContext, @NonNull Handler handler, @NonNull String tag, @NonNull Runnable runnable)2778 public WakeupMessage newWakeupMessage( 2779 @NonNull VcnContext vcnContext, 2780 @NonNull Handler handler, 2781 @NonNull String tag, 2782 @NonNull Runnable runnable) { 2783 return new WakeupMessage(vcnContext.getContext(), handler, tag, runnable); 2784 } 2785 2786 /** 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)2787 public VcnNetworkAgent newNetworkAgent( 2788 @NonNull VcnContext vcnContext, 2789 @NonNull String tag, 2790 @NonNull NetworkCapabilities caps, 2791 @NonNull LinkProperties lp, 2792 @NonNull NetworkScore score, 2793 @NonNull NetworkAgentConfig nac, 2794 @NonNull NetworkProvider provider, 2795 @NonNull Consumer<VcnNetworkAgent> networkUnwantedCallback, 2796 @NonNull Consumer<Integer> validationStatusCallback) { 2797 return new VcnNetworkAgent( 2798 vcnContext, 2799 tag, 2800 caps, 2801 lp, 2802 score, 2803 nac, 2804 provider, 2805 networkUnwantedCallback, 2806 validationStatusCallback); 2807 } 2808 2809 /** Checks if airplane mode is enabled. */ isAirplaneModeOn(@onNull VcnContext vcnContext)2810 public boolean isAirplaneModeOn(@NonNull VcnContext vcnContext) { 2811 return Settings.Global.getInt(vcnContext.getContext().getContentResolver(), 2812 Settings.Global.AIRPLANE_MODE_ON, 0) != 0; 2813 } 2814 2815 /** Gets the elapsed real time since boot, in millis. */ getElapsedRealTime()2816 public long getElapsedRealTime() { 2817 return SystemClock.elapsedRealtime(); 2818 } 2819 2820 /** Gets the MTU for the given underlying interface. */ getUnderlyingIfaceMtu(String ifaceName)2821 public int getUnderlyingIfaceMtu(String ifaceName) { 2822 try { 2823 final NetworkInterface underlyingIface = NetworkInterface.getByName(ifaceName); 2824 return underlyingIface == null ? 0 : underlyingIface.getMTU(); 2825 } catch (IOException e) { 2826 Slog.d(TAG, "Could not get MTU of underlying network", e); 2827 return 0; 2828 } 2829 } 2830 2831 /** Gets the max number of parallel tunnels allowed for tunnel aggregation. */ getParallelTunnelCount( TelephonySubscriptionSnapshot snapshot, ParcelUuid subGrp)2832 public int getParallelTunnelCount( 2833 TelephonySubscriptionSnapshot snapshot, ParcelUuid subGrp) { 2834 PersistableBundleWrapper carrierConfig = snapshot.getCarrierConfigForSubGrp(subGrp); 2835 int result = TUNNEL_AGGREGATION_SA_COUNT_MAX_DEFAULT; 2836 2837 if (carrierConfig != null) { 2838 result = 2839 carrierConfig.getInt( 2840 VcnManager.VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY, 2841 TUNNEL_AGGREGATION_SA_COUNT_MAX_DEFAULT); 2842 } 2843 2844 // Guard against tunnel count < 1 2845 return Math.max(1, result); 2846 } 2847 } 2848 2849 /** 2850 * Proxy implementation of Child Session Configuration, used for testing. 2851 * 2852 * <p>This wrapper allows mocking of the final, parcelable ChildSessionConfiguration object for 2853 * testing purposes. This is the unfortunate result of mockito-inline (for mocking final 2854 * classes) not working properly with system services & associated classes. 2855 * 2856 * <p>This class MUST EXCLUSIVELY be a passthrough, proxying calls directly to the actual 2857 * ChildSessionConfiguration. 2858 */ 2859 @VisibleForTesting(visibility = Visibility.PRIVATE) 2860 public static class VcnChildSessionConfiguration { 2861 private final ChildSessionConfiguration mChildConfig; 2862 VcnChildSessionConfiguration(ChildSessionConfiguration childConfig)2863 public VcnChildSessionConfiguration(ChildSessionConfiguration childConfig) { 2864 mChildConfig = childConfig; 2865 } 2866 2867 /** Retrieves the addresses to be used inside the tunnel. */ getInternalAddresses()2868 public List<LinkAddress> getInternalAddresses() { 2869 return mChildConfig.getInternalAddresses(); 2870 } 2871 2872 /** Retrieves the DNS servers to be used inside the tunnel. */ getInternalDnsServers()2873 public List<InetAddress> getInternalDnsServers() { 2874 return mChildConfig.getInternalDnsServers(); 2875 } 2876 } 2877 2878 /** Proxy implementation of IKE session, used for testing. */ 2879 @VisibleForTesting(visibility = Visibility.PRIVATE) 2880 public static class VcnIkeSession { 2881 private final IkeSession mImpl; 2882 VcnIkeSession( VcnContext vcnContext, IkeSessionParams ikeSessionParams, ChildSessionParams childSessionParams, IkeSessionCallback ikeSessionCallback, ChildSessionCallback childSessionCallback)2883 public VcnIkeSession( 2884 VcnContext vcnContext, 2885 IkeSessionParams ikeSessionParams, 2886 ChildSessionParams childSessionParams, 2887 IkeSessionCallback ikeSessionCallback, 2888 ChildSessionCallback childSessionCallback) { 2889 mImpl = 2890 new IkeSession( 2891 vcnContext.getContext(), 2892 ikeSessionParams, 2893 childSessionParams, 2894 new HandlerExecutor(new Handler(vcnContext.getLooper())), 2895 ikeSessionCallback, 2896 childSessionCallback); 2897 } 2898 2899 /** Creates a new IKE Child session. */ openChildSession( @onNull ChildSessionParams childSessionParams, @NonNull ChildSessionCallback childSessionCallback)2900 public void openChildSession( 2901 @NonNull ChildSessionParams childSessionParams, 2902 @NonNull ChildSessionCallback childSessionCallback) { 2903 mImpl.openChildSession(childSessionParams, childSessionCallback); 2904 } 2905 2906 /** Closes an IKE session as identified by the ChildSessionCallback. */ closeChildSession(@onNull ChildSessionCallback childSessionCallback)2907 public void closeChildSession(@NonNull ChildSessionCallback childSessionCallback) { 2908 mImpl.closeChildSession(childSessionCallback); 2909 } 2910 2911 /** Gracefully closes this IKE Session, waiting for remote acknowledgement. */ close()2912 public void close() { 2913 mImpl.close(); 2914 } 2915 2916 /** Forcibly kills this IKE Session, without waiting for a closure confirmation. */ kill()2917 public void kill() { 2918 mImpl.kill(); 2919 } 2920 2921 /** Sets the underlying network used by the IkeSession. */ setNetwork(@onNull Network network)2922 public void setNetwork(@NonNull Network network) { 2923 mImpl.setNetwork(network); 2924 } 2925 2926 /** Dumps the state of the IkeSession */ dump(@onNull IndentingPrintWriter pw)2927 public void dump(@NonNull IndentingPrintWriter pw) { 2928 mImpl.dump(pw); 2929 } 2930 } 2931 2932 /** Proxy Implementation of WakeLock, used for testing. */ 2933 @VisibleForTesting(visibility = Visibility.PRIVATE) 2934 public static class VcnWakeLock { 2935 private final WakeLock mImpl; 2936 VcnWakeLock(@onNull Context context, int flags, @NonNull String tag)2937 public VcnWakeLock(@NonNull Context context, int flags, @NonNull String tag) { 2938 final PowerManager powerManager = context.getSystemService(PowerManager.class); 2939 mImpl = powerManager.newWakeLock(flags, tag); 2940 mImpl.setReferenceCounted(false /* isReferenceCounted */); 2941 } 2942 2943 /** 2944 * Acquire this WakeLock. 2945 * 2946 * <p>Synchronize this action to minimize locking around WakeLock use. 2947 */ acquire()2948 public synchronized void acquire() { 2949 mImpl.acquire(); 2950 } 2951 2952 /** 2953 * Release this Wakelock. 2954 * 2955 * <p>Synchronize this action to minimize locking around WakeLock use. 2956 */ release()2957 public synchronized void release() { 2958 mImpl.release(); 2959 } 2960 } 2961 2962 /** Proxy Implementation of NetworkAgent, used for testing. */ 2963 @VisibleForTesting(visibility = Visibility.PRIVATE) 2964 public static class VcnNetworkAgent { 2965 private final NetworkAgent mImpl; 2966 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)2967 public VcnNetworkAgent( 2968 @NonNull VcnContext vcnContext, 2969 @NonNull String tag, 2970 @NonNull NetworkCapabilities caps, 2971 @NonNull LinkProperties lp, 2972 @NonNull NetworkScore score, 2973 @NonNull NetworkAgentConfig nac, 2974 @NonNull NetworkProvider provider, 2975 @NonNull Consumer<VcnNetworkAgent> networkUnwantedCallback, 2976 @NonNull Consumer<Integer> validationStatusCallback) { 2977 mImpl = 2978 new NetworkAgent( 2979 vcnContext.getContext(), 2980 vcnContext.getLooper(), 2981 tag, 2982 caps, 2983 lp, 2984 score, 2985 nac, 2986 provider) { 2987 @Override 2988 public void onNetworkUnwanted() { 2989 networkUnwantedCallback.accept(VcnNetworkAgent.this); 2990 } 2991 2992 @Override 2993 public void onValidationStatus(int status, @Nullable Uri redirectUri) { 2994 validationStatusCallback.accept(status); 2995 } 2996 }; 2997 } 2998 2999 /** Registers the underlying NetworkAgent */ register()3000 public void register() { 3001 mImpl.register(); 3002 } 3003 3004 /** Marks the underlying NetworkAgent as connected */ markConnected()3005 public void markConnected() { 3006 mImpl.markConnected(); 3007 } 3008 3009 /** Unregisters the underlying NetworkAgent */ unregister()3010 public void unregister() { 3011 mImpl.unregister(); 3012 } 3013 3014 /** Sends new NetworkCapabilities for the underlying NetworkAgent */ sendNetworkCapabilities(@onNull NetworkCapabilities caps)3015 public void sendNetworkCapabilities(@NonNull NetworkCapabilities caps) { 3016 mImpl.sendNetworkCapabilities(caps); 3017 } 3018 3019 /** Sends new LinkProperties for the underlying NetworkAgent */ sendLinkProperties(@onNull LinkProperties lp)3020 public void sendLinkProperties(@NonNull LinkProperties lp) { 3021 mImpl.sendLinkProperties(lp); 3022 } 3023 3024 /** Sends new NetworkCapabilities for the underlying NetworkAgent */ setUnderlyingNetworks(@ullable List<Network> underlyingNetworks)3025 public void setUnderlyingNetworks(@Nullable List<Network> underlyingNetworks) { 3026 mImpl.setUnderlyingNetworks(underlyingNetworks); 3027 } 3028 3029 /** Retrieves the Network for the underlying NetworkAgent */ 3030 @Nullable getNetwork()3031 public Network getNetwork() { 3032 return mImpl.getNetwork(); 3033 } 3034 } 3035 } 3036