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