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