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