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