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