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