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             logInfo(
919                     "Selected underlying network changed: "
920                             + (underlying == null ? null : underlying.network));
921 
922             // TODO(b/179091925): Move the delayed-message handling to BaseState
923 
924             // If underlying is null, all underlying networks have been lost. Disconnect VCN after a
925             // timeout (or immediately if in airplane mode, since the device user has indicated that
926             // the radios should all be turned off).
927             if (underlying == null) {
928                 if (mDeps.isAirplaneModeOn(mVcnContext)) {
929                     sendMessageAndAcquireWakeLock(
930                             EVENT_UNDERLYING_NETWORK_CHANGED,
931                             TOKEN_ALL,
932                             new EventUnderlyingNetworkChangedInfo(null));
933                     sendDisconnectRequestedAndAcquireWakelock(
934                             DISCONNECT_REASON_UNDERLYING_NETWORK_LOST, false /* shouldQuit */);
935                     return;
936                 }
937 
938                 setDisconnectRequestAlarm();
939             } else {
940                 // Received a new Network so any previous alarm is irrelevant - cancel + clear it,
941                 // and cancel any queued EVENT_DISCONNECT_REQUEST messages
942                 cancelDisconnectRequestAlarm();
943             }
944 
945             sendMessageAndAcquireWakeLock(
946                     EVENT_UNDERLYING_NETWORK_CHANGED,
947                     TOKEN_ALL,
948                     new EventUnderlyingNetworkChangedInfo(underlying));
949         }
950     }
951 
acquireWakeLock()952     private void acquireWakeLock() {
953         mVcnContext.ensureRunningOnLooperThread();
954 
955         if (!mIsQuitting.getValue()) {
956             mWakeLock.acquire();
957 
958             logVdbg("Wakelock acquired: " + mWakeLock);
959         }
960     }
961 
releaseWakeLock()962     private void releaseWakeLock() {
963         mVcnContext.ensureRunningOnLooperThread();
964 
965         mWakeLock.release();
966 
967         logVdbg("Wakelock released: " + mWakeLock);
968     }
969 
970     /**
971      * Attempt to release mWakeLock - this can only be done if the Handler is null (meaning the
972      * StateMachine has been shutdown and thus has no business keeping the WakeLock) or if there are
973      * no more messags left to process in the Handler queue (at which point the WakeLock can be
974      * released until more messages must be processed).
975      */
maybeReleaseWakeLock()976     private void maybeReleaseWakeLock() {
977         final Handler handler = getHandler();
978         if (handler == null || !handler.hasMessagesOrCallbacks()) {
979             releaseWakeLock();
980         }
981     }
982 
983     @Override
sendMessage(int what)984     public void sendMessage(int what) {
985         logWtf(
986                 "sendMessage should not be used in VcnGatewayConnection. See"
987                         + " sendMessageAndAcquireWakeLock()");
988         super.sendMessage(what);
989     }
990 
991     @Override
sendMessage(int what, Object obj)992     public void sendMessage(int what, Object obj) {
993         logWtf(
994                 "sendMessage should not be used in VcnGatewayConnection. See"
995                         + " sendMessageAndAcquireWakeLock()");
996         super.sendMessage(what, obj);
997     }
998 
999     @Override
sendMessage(int what, int arg1)1000     public void sendMessage(int what, int arg1) {
1001         logWtf(
1002                 "sendMessage should not be used in VcnGatewayConnection. See"
1003                         + " sendMessageAndAcquireWakeLock()");
1004         super.sendMessage(what, arg1);
1005     }
1006 
1007     @Override
sendMessage(int what, int arg1, int arg2)1008     public void sendMessage(int what, int arg1, int arg2) {
1009         logWtf(
1010                 "sendMessage should not be used in VcnGatewayConnection. See"
1011                         + " sendMessageAndAcquireWakeLock()");
1012         super.sendMessage(what, arg1, arg2);
1013     }
1014 
1015     @Override
sendMessage(int what, int arg1, int arg2, Object obj)1016     public void sendMessage(int what, int arg1, int arg2, Object obj) {
1017         logWtf(
1018                 "sendMessage should not be used in VcnGatewayConnection. See"
1019                         + " sendMessageAndAcquireWakeLock()");
1020         super.sendMessage(what, arg1, arg2, obj);
1021     }
1022 
1023     @Override
sendMessage(Message msg)1024     public void sendMessage(Message msg) {
1025         logWtf(
1026                 "sendMessage should not be used in VcnGatewayConnection. See"
1027                         + " sendMessageAndAcquireWakeLock()");
1028         super.sendMessage(msg);
1029     }
1030 
1031     // TODO(b/180146061): also override and Log.wtf() other Message handling methods
1032     // In mind are sendMessageDelayed(), sendMessageAtFrontOfQueue, removeMessages, and
1033     // removeDeferredMessages
1034 
1035     /**
1036      * WakeLock-based alternative to {@link #sendMessage}. Use to guarantee that the device will not
1037      * go to sleep before processing the sent message.
1038      */
sendMessageAndAcquireWakeLock(int what, int token)1039     private void sendMessageAndAcquireWakeLock(int what, int token) {
1040         acquireWakeLock();
1041         super.sendMessage(what, token);
1042     }
1043 
1044     /**
1045      * WakeLock-based alternative to {@link #sendMessage}. Use to guarantee that the device will not
1046      * go to sleep before processing the sent message.
1047      */
sendMessageAndAcquireWakeLock(int what, int token, EventInfo data)1048     private void sendMessageAndAcquireWakeLock(int what, int token, EventInfo data) {
1049         acquireWakeLock();
1050         super.sendMessage(what, token, ARG_NOT_PRESENT, data);
1051     }
1052 
1053     /**
1054      * WakeLock-based alternative to {@link #sendMessage}. Use to guarantee that the device will not
1055      * go to sleep before processing the sent message.
1056      */
sendMessageAndAcquireWakeLock(int what, int token, int arg2, EventInfo data)1057     private void sendMessageAndAcquireWakeLock(int what, int token, int arg2, EventInfo data) {
1058         acquireWakeLock();
1059         super.sendMessage(what, token, arg2, data);
1060     }
1061 
1062     /**
1063      * WakeLock-based alternative to {@link #sendMessage}. Use to guarantee that the device will not
1064      * go to sleep before processing the sent message.
1065      */
sendMessageAndAcquireWakeLock(Message msg)1066     private void sendMessageAndAcquireWakeLock(Message msg) {
1067         acquireWakeLock();
1068         super.sendMessage(msg);
1069     }
1070 
1071     /**
1072      * Removes all messages matching the given parameters, and attempts to release mWakeLock if the
1073      * Handler is empty.
1074      *
1075      * @param what the Message.what value to be removed
1076      */
removeEqualMessages(int what)1077     private void removeEqualMessages(int what) {
1078         removeEqualMessages(what, null /* obj */);
1079     }
1080 
1081     /**
1082      * Removes all messages matching the given parameters, and attempts to release mWakeLock if the
1083      * Handler is empty.
1084      *
1085      * @param what the Message.what value to be removed
1086      * @param obj the Message.obj to to be removed, or null if all messages matching Message.what
1087      *     should be removed
1088      */
removeEqualMessages(int what, @Nullable Object obj)1089     private void removeEqualMessages(int what, @Nullable Object obj) {
1090         final Handler handler = getHandler();
1091         if (handler != null) {
1092             handler.removeEqualMessages(what, obj);
1093         }
1094 
1095         maybeReleaseWakeLock();
1096     }
1097 
createScheduledAlarm( @onNull String cmdName, Message delayedMessage, long delay)1098     private WakeupMessage createScheduledAlarm(
1099             @NonNull String cmdName, Message delayedMessage, long delay) {
1100         final Handler handler = getHandler();
1101         if (handler == null) {
1102             logWarn(
1103                     "Attempted to schedule alarm after StateMachine has quit",
1104                     new IllegalStateException());
1105             return null; // StateMachine has already quit.
1106         }
1107 
1108         // WakeupMessage uses Handler#dispatchMessage() to immediately handle the specified Runnable
1109         // at the scheduled time. dispatchMessage() immediately executes and there may be queued
1110         // events that resolve the scheduled alarm pending in the queue. So, use the Runnable to
1111         // place the alarm event at the end of the queue with sendMessageAndAcquireWakeLock (which
1112         // guarantees the device will stay awake).
1113         final WakeupMessage alarm =
1114                 mDeps.newWakeupMessage(
1115                         mVcnContext,
1116                         handler,
1117                         cmdName,
1118                         () -> sendMessageAndAcquireWakeLock(delayedMessage));
1119         alarm.schedule(mDeps.getElapsedRealTime() + delay);
1120         return alarm;
1121     }
1122 
setTeardownTimeoutAlarm()1123     private void setTeardownTimeoutAlarm() {
1124         logVdbg("Setting teardown timeout alarm; mCurrentToken: " + mCurrentToken);
1125 
1126         // Safe to assign this alarm because it is either 1) already null, or 2) already fired. In
1127         // either case, there is nothing to cancel.
1128         if (mTeardownTimeoutAlarm != null) {
1129             logWtf(
1130                     "mTeardownTimeoutAlarm should be null before being set; mCurrentToken: "
1131                             + mCurrentToken);
1132         }
1133 
1134         final Message delayedMessage = obtainMessage(EVENT_TEARDOWN_TIMEOUT_EXPIRED, mCurrentToken);
1135         mTeardownTimeoutAlarm =
1136                 createScheduledAlarm(
1137                         TEARDOWN_TIMEOUT_ALARM,
1138                         delayedMessage,
1139                         TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS));
1140     }
1141 
cancelTeardownTimeoutAlarm()1142     private void cancelTeardownTimeoutAlarm() {
1143         logVdbg("Cancelling teardown timeout alarm; mCurrentToken: " + mCurrentToken);
1144 
1145         if (mTeardownTimeoutAlarm != null) {
1146             mTeardownTimeoutAlarm.cancel();
1147             mTeardownTimeoutAlarm = null;
1148         }
1149 
1150         // Cancel any existing teardown timeouts
1151         removeEqualMessages(EVENT_TEARDOWN_TIMEOUT_EXPIRED);
1152     }
1153 
setDisconnectRequestAlarm()1154     private void setDisconnectRequestAlarm() {
1155         logVdbg(
1156                 "Setting alarm to disconnect due to underlying network loss;"
1157                         + " mCurrentToken: "
1158                         + mCurrentToken);
1159 
1160         // Only schedule a NEW alarm if none is already set.
1161         if (mDisconnectRequestAlarm != null) {
1162             return;
1163         }
1164 
1165         final Message delayedMessage =
1166                 obtainMessage(
1167                         EVENT_DISCONNECT_REQUESTED,
1168                         TOKEN_ALL,
1169                         0 /* arg2 */,
1170                         new EventDisconnectRequestedInfo(
1171                                 DISCONNECT_REASON_UNDERLYING_NETWORK_LOST, false /* shouldQuit */));
1172         mDisconnectRequestAlarm =
1173                 createScheduledAlarm(
1174                         DISCONNECT_REQUEST_ALARM,
1175                         delayedMessage,
1176                         TimeUnit.SECONDS.toMillis(NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS));
1177     }
1178 
cancelDisconnectRequestAlarm()1179     private void cancelDisconnectRequestAlarm() {
1180         logVdbg(
1181                 "Cancelling alarm to disconnect due to underlying network loss;"
1182                         + " mCurrentToken: "
1183                         + mCurrentToken);
1184 
1185         if (mDisconnectRequestAlarm != null) {
1186             mDisconnectRequestAlarm.cancel();
1187             mDisconnectRequestAlarm = null;
1188         }
1189 
1190         // Cancel any existing disconnect due to previous loss of underlying network
1191         removeEqualMessages(
1192                 EVENT_DISCONNECT_REQUESTED,
1193                 new EventDisconnectRequestedInfo(
1194                         DISCONNECT_REASON_UNDERLYING_NETWORK_LOST, false /* shouldQuit */));
1195     }
1196 
setRetryTimeoutAlarm(long delay)1197     private void setRetryTimeoutAlarm(long delay) {
1198         logVdbg("Setting retry alarm; mCurrentToken: " + mCurrentToken);
1199 
1200         // Safe to assign this alarm because it is either 1) already null, or 2) already fired. In
1201         // either case, there is nothing to cancel.
1202         if (mRetryTimeoutAlarm != null) {
1203             logWtf(
1204                     "mRetryTimeoutAlarm should be null before being set; mCurrentToken: "
1205                             + mCurrentToken);
1206         }
1207 
1208         final Message delayedMessage = obtainMessage(EVENT_RETRY_TIMEOUT_EXPIRED, mCurrentToken);
1209         mRetryTimeoutAlarm = createScheduledAlarm(RETRY_TIMEOUT_ALARM, delayedMessage, delay);
1210     }
1211 
cancelRetryTimeoutAlarm()1212     private void cancelRetryTimeoutAlarm() {
1213         logVdbg("Cancel retry alarm; mCurrentToken: " + mCurrentToken);
1214 
1215         if (mRetryTimeoutAlarm != null) {
1216             mRetryTimeoutAlarm.cancel();
1217             mRetryTimeoutAlarm = null;
1218         }
1219 
1220         removeEqualMessages(EVENT_RETRY_TIMEOUT_EXPIRED);
1221     }
1222 
1223     @VisibleForTesting(visibility = Visibility.PRIVATE)
setSafeModeAlarm()1224     void setSafeModeAlarm() {
1225         logVdbg("Setting safe mode alarm; mCurrentToken: " + mCurrentToken);
1226 
1227         // Only schedule a NEW alarm if none is already set.
1228         if (mSafeModeTimeoutAlarm != null) {
1229             return;
1230         }
1231 
1232         final Message delayedMessage = obtainMessage(EVENT_SAFE_MODE_TIMEOUT_EXCEEDED, TOKEN_ALL);
1233         mSafeModeTimeoutAlarm =
1234                 createScheduledAlarm(
1235                         SAFEMODE_TIMEOUT_ALARM,
1236                         delayedMessage,
1237                         mVcnContext.isInTestMode()
1238                                 ? TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS_TEST_MODE)
1239                                 : TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS));
1240     }
1241 
cancelSafeModeAlarm()1242     private void cancelSafeModeAlarm() {
1243         logVdbg("Cancel safe mode alarm; mCurrentToken: " + mCurrentToken);
1244 
1245         if (mSafeModeTimeoutAlarm != null) {
1246             mSafeModeTimeoutAlarm.cancel();
1247             mSafeModeTimeoutAlarm = null;
1248         }
1249 
1250         removeEqualMessages(EVENT_SAFE_MODE_TIMEOUT_EXCEEDED);
1251     }
1252 
sessionLostWithoutCallback(int token, @Nullable Exception exception)1253     private void sessionLostWithoutCallback(int token, @Nullable Exception exception) {
1254         sendMessageAndAcquireWakeLock(
1255                 EVENT_SESSION_LOST, token, new EventSessionLostInfo(exception));
1256     }
1257 
sessionLost(int token, @Nullable Exception exception)1258     private void sessionLost(int token, @Nullable Exception exception) {
1259         // Only notify mGatewayStatusCallback if the session was lost with an error. All
1260         // authentication and DNS failures are sent through
1261         // IkeSessionCallback.onClosedExceptionally(), which calls sessionClosed()
1262         if (exception != null) {
1263             mGatewayStatusCallback.onGatewayConnectionError(
1264                     mConnectionConfig.getGatewayConnectionName(),
1265                     VCN_ERROR_CODE_INTERNAL_ERROR,
1266                     RuntimeException.class.getName(),
1267                     "Received "
1268                             + exception.getClass().getSimpleName()
1269                             + " with message: "
1270                             + exception.getMessage());
1271         }
1272 
1273         sessionLostWithoutCallback(token, exception);
1274     }
1275 
isIkeAuthFailure(@onNull Exception exception)1276     private static boolean isIkeAuthFailure(@NonNull Exception exception) {
1277         if (!(exception instanceof IkeProtocolException)) {
1278             return false;
1279         }
1280 
1281         return ((IkeProtocolException) exception).getErrorType()
1282                 == ERROR_TYPE_AUTHENTICATION_FAILED;
1283     }
1284 
notifyStatusCallbackForSessionClosed(@onNull Exception exception)1285     private void notifyStatusCallbackForSessionClosed(@NonNull Exception exception) {
1286         final int errorCode;
1287         final String exceptionClass;
1288         final String exceptionMessage;
1289 
1290         if (isIkeAuthFailure(exception)) {
1291             errorCode = VCN_ERROR_CODE_CONFIG_ERROR;
1292             exceptionClass = exception.getClass().getName();
1293             exceptionMessage = exception.getMessage();
1294         } else if (exception instanceof IkeInternalException
1295                 && exception.getCause() instanceof IOException) {
1296             errorCode = VCN_ERROR_CODE_NETWORK_ERROR;
1297             exceptionClass = IOException.class.getName();
1298             exceptionMessage = exception.getCause().getMessage();
1299         } else {
1300             errorCode = VCN_ERROR_CODE_INTERNAL_ERROR;
1301             exceptionClass = RuntimeException.class.getName();
1302             exceptionMessage =
1303                     "Received "
1304                             + exception.getClass().getSimpleName()
1305                             + " with message: "
1306                             + exception.getMessage();
1307         }
1308 
1309         logDbg(
1310                 "Encountered error; code="
1311                         + errorCode
1312                         + ", exceptionClass="
1313                         + exceptionClass
1314                         + ", exceptionMessage="
1315                         + exceptionMessage);
1316 
1317         mGatewayStatusCallback.onGatewayConnectionError(
1318                 mConnectionConfig.getGatewayConnectionName(),
1319                 errorCode,
1320                 exceptionClass,
1321                 exceptionMessage);
1322     }
1323 
ikeConnectionInfoChanged( int token, @NonNull IkeSessionConnectionInfo ikeConnectionInfo)1324     private void ikeConnectionInfoChanged(
1325             int token, @NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
1326         sendMessageAndAcquireWakeLock(
1327                 EVENT_IKE_CONNECTION_INFO_CHANGED,
1328                 token,
1329                 new EventIkeConnectionInfoChangedInfo(ikeConnectionInfo));
1330     }
1331 
sessionClosed(int token, @Nullable Exception exception)1332     private void sessionClosed(int token, @Nullable Exception exception) {
1333         if (exception != null) {
1334             notifyStatusCallbackForSessionClosed(exception);
1335         }
1336 
1337         // SESSION_LOST MUST be sent before SESSION_CLOSED to ensure that the SM moves to the
1338         // Disconnecting state.
1339         sessionLostWithoutCallback(token, exception);
1340         sendMessageAndAcquireWakeLock(EVENT_SESSION_CLOSED, token);
1341     }
1342 
migrationCompleted( int token, @NonNull IpSecTransform inTransform, @NonNull IpSecTransform outTransform)1343     private void migrationCompleted(
1344             int token, @NonNull IpSecTransform inTransform, @NonNull IpSecTransform outTransform) {
1345         sendMessageAndAcquireWakeLock(
1346                 EVENT_MIGRATION_COMPLETED,
1347                 token,
1348                 new EventMigrationCompletedInfo(inTransform, outTransform));
1349     }
1350 
childTransformCreated( int token, @NonNull IpSecTransform transform, int direction)1351     private void childTransformCreated(
1352             int token, @NonNull IpSecTransform transform, int direction) {
1353         sendMessageAndAcquireWakeLock(
1354                 EVENT_TRANSFORM_CREATED,
1355                 token,
1356                 new EventTransformCreatedInfo(direction, transform));
1357     }
1358 
childOpened(int token, @NonNull VcnChildSessionConfiguration childConfig)1359     private void childOpened(int token, @NonNull VcnChildSessionConfiguration childConfig) {
1360         sendMessageAndAcquireWakeLock(
1361                 EVENT_SETUP_COMPLETED, token, new EventSetupCompletedInfo(childConfig));
1362     }
1363 
1364     private abstract class BaseState extends State {
1365         @Override
enter()1366         public void enter() {
1367             try {
1368                 enterState();
1369             } catch (Exception e) {
1370                 logWtf("Uncaught exception", e);
1371                 sendDisconnectRequestedAndAcquireWakelock(
1372                         DISCONNECT_REASON_INTERNAL_ERROR + e.toString(), true /* shouldQuit */);
1373             }
1374         }
1375 
enterState()1376         protected void enterState() throws Exception {}
1377 
1378         /**
1379          * Returns whether the given token is valid.
1380          *
1381          * <p>By default, States consider any and all token to be 'valid'.
1382          *
1383          * <p>States should override this method if they want to restrict message handling to
1384          * specific tokens.
1385          */
isValidToken(int token)1386         protected boolean isValidToken(int token) {
1387             return true;
1388         }
1389 
1390         /**
1391          * Top-level processMessage with safeguards to prevent crashing the System Server on non-eng
1392          * builds.
1393          *
1394          * <p>Here be dragons: processMessage() is final to ensure that mWakeLock is released once
1395          * the Handler queue is empty. Future changes (or overrides) to processMessage() to MUST
1396          * ensure that mWakeLock is correctly released.
1397          */
1398         @Override
processMessage(Message msg)1399         public final boolean processMessage(Message msg) {
1400             final int token = msg.arg1;
1401             if (!isValidToken(token)) {
1402                 logDbg("Message called with obsolete token: " + token + "; what: " + msg.what);
1403                 return HANDLED;
1404             }
1405 
1406             try {
1407                 processStateMsg(msg);
1408             } catch (Exception e) {
1409                 logWtf("Uncaught exception", e);
1410                 sendDisconnectRequestedAndAcquireWakelock(
1411                         DISCONNECT_REASON_INTERNAL_ERROR + e.toString(), true /* shouldQuit */);
1412             }
1413 
1414             // Attempt to release the WakeLock - only possible if the Handler queue is empty
1415             maybeReleaseWakeLock();
1416 
1417             return HANDLED;
1418         }
1419 
processStateMsg(Message msg)1420         protected abstract void processStateMsg(Message msg) throws Exception;
1421 
1422         @Override
exit()1423         public void exit() {
1424             try {
1425                 exitState();
1426             } catch (Exception e) {
1427                 logWtf("Uncaught exception", e);
1428                 sendDisconnectRequestedAndAcquireWakelock(
1429                         DISCONNECT_REASON_INTERNAL_ERROR + e.toString(), true /* shouldQuit */);
1430             }
1431         }
1432 
exitState()1433         protected void exitState() throws Exception {}
1434 
logUnhandledMessage(Message msg)1435         protected void logUnhandledMessage(Message msg) {
1436             // Log as unexpected all known messages, and log all else as unknown.
1437             switch (msg.what) {
1438                 case EVENT_UNDERLYING_NETWORK_CHANGED: // Fallthrough
1439                 case EVENT_RETRY_TIMEOUT_EXPIRED: // Fallthrough
1440                 case EVENT_SESSION_LOST: // Fallthrough
1441                 case EVENT_SESSION_CLOSED: // Fallthrough
1442                 case EVENT_TRANSFORM_CREATED: // Fallthrough
1443                 case EVENT_SETUP_COMPLETED: // Fallthrough
1444                 case EVENT_DISCONNECT_REQUESTED: // Fallthrough
1445                 case EVENT_TEARDOWN_TIMEOUT_EXPIRED: // Fallthrough
1446                 case EVENT_SUBSCRIPTIONS_CHANGED: // Fallthrough
1447                 case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED: // Fallthrough
1448                 case EVENT_MIGRATION_COMPLETED: // Fallthrough
1449                 case EVENT_IKE_CONNECTION_INFO_CHANGED: // Fallthrough
1450                 case EVENT_DATA_STALL_SUSPECTED:
1451                     logUnexpectedEvent(msg.what);
1452                     break;
1453                 default:
1454                     logWtfUnknownEvent(msg.what);
1455                     break;
1456             }
1457         }
1458 
teardownNetwork()1459         protected void teardownNetwork() {
1460             if (mNetworkAgent != null) {
1461                 mNetworkAgent.unregister();
1462                 mNetworkAgent = null;
1463             }
1464         }
1465 
handleDisconnectRequested(EventDisconnectRequestedInfo info)1466         protected void handleDisconnectRequested(EventDisconnectRequestedInfo info) {
1467             // TODO(b/180526152): notify VcnStatusCallback for Network loss
1468 
1469             logInfo("Tearing down. Cause: " + info.reason + "; quitting = " + info.shouldQuit);
1470             if (info.shouldQuit) {
1471                 mIsQuitting.setTrue();
1472             }
1473 
1474             teardownNetwork();
1475 
1476             if (mIkeSession == null) {
1477                 // Already disconnected, go straight to DisconnectedState
1478                 transitionTo(mDisconnectedState);
1479             } else {
1480                 // Still need to wait for full closure
1481                 transitionTo(mDisconnectingState);
1482             }
1483         }
1484 
handleSafeModeTimeoutExceeded()1485         protected void handleSafeModeTimeoutExceeded() {
1486             mSafeModeTimeoutAlarm = null;
1487             logInfo("Entering safe mode after timeout exceeded");
1488 
1489             // Connectivity for this GatewayConnection is broken; tear down the Network.
1490             teardownNetwork();
1491             mIsInSafeMode = true;
1492             mGatewayStatusCallback.onSafeModeStatusChanged();
1493         }
1494 
logUnexpectedEvent(int what)1495         protected void logUnexpectedEvent(int what) {
1496             logVdbg(
1497                     "Unexpected event code "
1498                             + what
1499                             + " in state "
1500                             + this.getClass().getSimpleName());
1501         }
1502 
logWtfUnknownEvent(int what)1503         protected void logWtfUnknownEvent(int what) {
1504             logWtf("Unknown event code " + what + " in state " + this.getClass().getSimpleName());
1505         }
1506     }
1507 
1508     /**
1509      * State representing the a disconnected VCN tunnel.
1510      *
1511      * <p>This is also is the initial state.
1512      */
1513     private class DisconnectedState extends BaseState {
1514         @Override
enterState()1515         protected void enterState() {
1516             if (mIsQuitting.getValue()) {
1517                 quitNow(); // Ignore all queued events; cleanup is complete.
1518             }
1519 
1520             if (mIkeSession != null || mNetworkAgent != null) {
1521                 logWtf("Active IKE Session or NetworkAgent in DisconnectedState");
1522             }
1523 
1524             cancelSafeModeAlarm();
1525         }
1526 
1527         @Override
processStateMsg(Message msg)1528         protected void processStateMsg(Message msg) {
1529             switch (msg.what) {
1530                 case EVENT_UNDERLYING_NETWORK_CHANGED:
1531                     // First network found; start tunnel
1532                     mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
1533 
1534                     if (mUnderlying != null) {
1535                         transitionTo(mConnectingState);
1536                     }
1537                     break;
1538                 case EVENT_DISCONNECT_REQUESTED:
1539                     if (((EventDisconnectRequestedInfo) msg.obj).shouldQuit) {
1540                         mIsQuitting.setTrue();
1541 
1542                         quitNow();
1543                     }
1544                     break;
1545                 default:
1546                     logUnhandledMessage(msg);
1547                     break;
1548             }
1549         }
1550 
1551         @Override
exitState()1552         protected void exitState() {
1553             // Safe to blindly set up, as it is cancelled and cleared on entering this state
1554             setSafeModeAlarm();
1555         }
1556     }
1557 
1558     private abstract class ActiveBaseState extends BaseState {
1559         @Override
isValidToken(int token)1560         protected boolean isValidToken(int token) {
1561             return (token == TOKEN_ALL || token == mCurrentToken);
1562         }
1563     }
1564 
1565     /**
1566      * Transitive state representing a VCN that is tearing down an IKE session.
1567      *
1568      * <p>In this state, the IKE session is in the process of being torn down. If the IKE session
1569      * does not complete teardown in a timely fashion, it will be killed (forcibly closed).
1570      */
1571     private class DisconnectingState extends ActiveBaseState {
1572         /**
1573          * Whether to skip the RetryTimeoutState and go straight to the ConnectingState.
1574          *
1575          * <p>This is used when an underlying network change triggered a restart on a new network.
1576          *
1577          * <p>Reset (to false) upon exit of the DisconnectingState.
1578          */
1579         private boolean mSkipRetryTimeout = false;
1580 
1581         // TODO(b/178441390): Remove this in favor of resetting retry timers on UND_NET change.
setSkipRetryTimeout(boolean shouldSkip)1582         public void setSkipRetryTimeout(boolean shouldSkip) {
1583             mSkipRetryTimeout = shouldSkip;
1584         }
1585 
1586         @Override
enterState()1587         protected void enterState() throws Exception {
1588             if (mIkeSession == null) {
1589                 logWtf("IKE session was already closed when entering Disconnecting state.");
1590                 sendMessageAndAcquireWakeLock(EVENT_SESSION_CLOSED, mCurrentToken);
1591                 return;
1592             }
1593 
1594             // If underlying network has already been lost, save some time and just kill the session
1595             if (mUnderlying == null) {
1596                 // Will trigger a EVENT_SESSION_CLOSED as IkeSession shuts down.
1597                 mIkeSession.kill();
1598                 return;
1599             }
1600 
1601             mIkeSession.close();
1602 
1603             // Safe to blindly set up, as it is cancelled and cleared on exiting this state
1604             setTeardownTimeoutAlarm();
1605         }
1606 
1607         @Override
processStateMsg(Message msg)1608         protected void processStateMsg(Message msg) {
1609             switch (msg.what) {
1610                 case EVENT_UNDERLYING_NETWORK_CHANGED: // Fallthrough
1611                     mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
1612 
1613                     // If we received a new underlying network, continue.
1614                     if (mUnderlying != null) {
1615                         break;
1616                     }
1617 
1618                     // Fallthrough; no network exists to send IKE close session requests.
1619                 case EVENT_TEARDOWN_TIMEOUT_EXPIRED:
1620                     // Grace period ended. Kill session, triggering EVENT_SESSION_CLOSED
1621                     mIkeSession.kill();
1622 
1623                     break;
1624                 case EVENT_DISCONNECT_REQUESTED:
1625                     EventDisconnectRequestedInfo info = ((EventDisconnectRequestedInfo) msg.obj);
1626                     if (info.shouldQuit) {
1627                         mIsQuitting.setTrue();
1628                     }
1629 
1630                     teardownNetwork();
1631 
1632                     if (info.reason.equals(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)) {
1633                         // TODO(b/180526152): notify VcnStatusCallback for Network loss
1634 
1635                         // Will trigger EVENT_SESSION_CLOSED immediately.
1636                         mIkeSession.kill();
1637                         break;
1638                     }
1639 
1640                     // Otherwise we are already in the process of shutting down.
1641                     break;
1642                 case EVENT_SESSION_CLOSED:
1643                     mIkeSession = null;
1644 
1645                     if (!mIsQuitting.getValue() && mUnderlying != null) {
1646                         transitionTo(mSkipRetryTimeout ? mConnectingState : mRetryTimeoutState);
1647                     } else {
1648                         teardownNetwork();
1649                         transitionTo(mDisconnectedState);
1650                     }
1651                     break;
1652                 case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
1653                     handleSafeModeTimeoutExceeded();
1654                     break;
1655                 default:
1656                     logUnhandledMessage(msg);
1657                     break;
1658             }
1659         }
1660 
1661         @Override
exitState()1662         protected void exitState() throws Exception {
1663             mSkipRetryTimeout = false;
1664 
1665             cancelTeardownTimeoutAlarm();
1666         }
1667     }
1668 
1669     /**
1670      * Transitive state representing a VCN that is making an primary (non-handover) connection.
1671      *
1672      * <p>This state starts IKE negotiation, but defers transform application & network setup to the
1673      * Connected state.
1674      */
1675     private class ConnectingState extends ActiveBaseState {
1676         @Override
enterState()1677         protected void enterState() {
1678             if (mIkeSession != null) {
1679                 logWtf("ConnectingState entered with active session");
1680 
1681                 // Attempt to recover.
1682                 mIkeSession.kill();
1683                 mIkeSession = null;
1684             }
1685 
1686             mIkeSession = buildIkeSession(mUnderlying.network);
1687         }
1688 
1689         @Override
processStateMsg(Message msg)1690         protected void processStateMsg(Message msg) {
1691             switch (msg.what) {
1692                 case EVENT_UNDERLYING_NETWORK_CHANGED:
1693                     final UnderlyingNetworkRecord oldUnderlying = mUnderlying;
1694                     mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
1695 
1696                     if (oldUnderlying == null) {
1697                         // This should never happen, but if it does, there's likely a nasty bug.
1698                         logWtf("Old underlying network was null in connected state. Bug?");
1699                     }
1700 
1701                     // If new underlying is null, all underlying networks have been lost; disconnect
1702                     if (mUnderlying == null) {
1703                         transitionTo(mDisconnectingState);
1704                         break;
1705                     }
1706 
1707                     if (oldUnderlying != null
1708                             && mUnderlying.network.equals(oldUnderlying.network)) {
1709                         break; // Only network properties have changed; continue connecting.
1710                     }
1711                     // Else, retry on the new network.
1712 
1713                     // Immediately come back to the ConnectingState (skip RetryTimeout, since this
1714                     // isn't a failure)
1715                     mDisconnectingState.setSkipRetryTimeout(true);
1716 
1717                     // fallthrough - disconnect, and retry on new network.
1718                 case EVENT_SESSION_LOST:
1719                     transitionTo(mDisconnectingState);
1720                     break;
1721                 case EVENT_SESSION_CLOSED:
1722                     // Disconnecting state waits for EVENT_SESSION_CLOSED to shutdown, and this
1723                     // message may not be posted again. Defer to ensure immediate shutdown.
1724                     deferMessage(msg);
1725 
1726                     transitionTo(mDisconnectingState);
1727                     break;
1728                 case EVENT_SETUP_COMPLETED: // fallthrough
1729                 case EVENT_IKE_CONNECTION_INFO_CHANGED: // fallthrough
1730                 case EVENT_TRANSFORM_CREATED:
1731                     // Child setup complete; move to ConnectedState for NetworkAgent registration
1732                     deferMessage(msg);
1733                     transitionTo(mConnectedState);
1734                     break;
1735                 case EVENT_DISCONNECT_REQUESTED:
1736                     handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj);
1737                     break;
1738                 case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
1739                     handleSafeModeTimeoutExceeded();
1740                     break;
1741                 default:
1742                     logUnhandledMessage(msg);
1743                     break;
1744             }
1745         }
1746     }
1747 
1748     private abstract class ConnectedStateBase extends ActiveBaseState {
updateNetworkAgent( @onNull IpSecTunnelInterface tunnelIface, @NonNull VcnNetworkAgent agent, @NonNull VcnChildSessionConfiguration childConfig, @NonNull IkeSessionConnectionInfo ikeConnectionInfo)1749         protected void updateNetworkAgent(
1750                 @NonNull IpSecTunnelInterface tunnelIface,
1751                 @NonNull VcnNetworkAgent agent,
1752                 @NonNull VcnChildSessionConfiguration childConfig,
1753                 @NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
1754             final NetworkCapabilities caps =
1755                     buildNetworkCapabilities(mConnectionConfig, mUnderlying, mIsMobileDataEnabled);
1756             final LinkProperties lp =
1757                     buildConnectedLinkProperties(
1758                             mConnectionConfig,
1759                             tunnelIface,
1760                             childConfig,
1761                             mUnderlying,
1762                             ikeConnectionInfo);
1763 
1764             agent.sendNetworkCapabilities(caps);
1765             agent.sendLinkProperties(lp);
1766 
1767             agent.setUnderlyingNetworks(
1768                     mUnderlying == null ? null : Collections.singletonList(mUnderlying.network));
1769         }
1770 
buildNetworkAgent( @onNull IpSecTunnelInterface tunnelIface, @NonNull VcnChildSessionConfiguration childConfig, @NonNull IkeSessionConnectionInfo ikeConnectionInfo)1771         protected VcnNetworkAgent buildNetworkAgent(
1772                 @NonNull IpSecTunnelInterface tunnelIface,
1773                 @NonNull VcnChildSessionConfiguration childConfig,
1774                 @NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
1775             final NetworkCapabilities caps =
1776                     buildNetworkCapabilities(mConnectionConfig, mUnderlying, mIsMobileDataEnabled);
1777             final LinkProperties lp =
1778                     buildConnectedLinkProperties(
1779                             mConnectionConfig,
1780                             tunnelIface,
1781                             childConfig,
1782                             mUnderlying,
1783                             ikeConnectionInfo);
1784             final NetworkAgentConfig nac =
1785                     new NetworkAgentConfig.Builder()
1786                             .setLegacyType(ConnectivityManager.TYPE_MOBILE)
1787                             .setLegacyTypeName(NETWORK_INFO_NETWORK_TYPE_STRING)
1788                             .setLegacySubType(TelephonyManager.NETWORK_TYPE_UNKNOWN)
1789                             .setLegacySubTypeName(
1790                                     TelephonyManager.getNetworkTypeName(
1791                                             TelephonyManager.NETWORK_TYPE_UNKNOWN))
1792                             .setLegacyExtraInfo(NETWORK_INFO_EXTRA_INFO)
1793                             .build();
1794 
1795             final VcnNetworkAgent agent =
1796                     mDeps.newNetworkAgent(
1797                             mVcnContext,
1798                             TAG,
1799                             caps,
1800                             lp,
1801                             Vcn.getNetworkScore(),
1802                             nac,
1803                             mVcnContext.getVcnNetworkProvider(),
1804                             (agentRef) -> {
1805                                 // Only trigger teardown if the NetworkAgent hasn't been replaced or
1806                                 // changed. This guards against two cases - the first where
1807                                 // unwanted() may be called as a result of the
1808                                 // NetworkAgent.unregister() call, which might trigger a teardown
1809                                 // instead of just a Network disconnect, as well as the case where a
1810                                 // new NetworkAgent replaces an old one before the unwanted() call
1811                                 // is processed.
1812                                 if (mNetworkAgent != agentRef) {
1813                                     logDbg("unwanted() called on stale NetworkAgent");
1814                                     return;
1815                                 }
1816 
1817                                 logInfo("NetworkAgent was unwanted");
1818                                 teardownAsynchronously();
1819                             } /* networkUnwantedCallback */,
1820                             (status) -> {
1821                                 if (mIsQuitting.getValue()) {
1822                                     return; // Ignore; VcnGatewayConnection quitting or already quit
1823                                 }
1824 
1825                                 switch (status) {
1826                                     case NetworkAgent.VALIDATION_STATUS_VALID:
1827                                         clearFailedAttemptCounterAndSafeModeAlarm();
1828                                         break;
1829                                     case NetworkAgent.VALIDATION_STATUS_NOT_VALID:
1830                                         // Trigger re-validation of underlying networks; if it
1831                                         // fails, the VCN will attempt to migrate away.
1832                                         if (mUnderlying != null) {
1833                                             mConnectivityManager.reportNetworkConnectivity(
1834                                                     mUnderlying.network,
1835                                                     false /* hasConnectivity */);
1836                                         }
1837 
1838                                         // Will only set a new alarm if no safe mode alarm is
1839                                         // currently scheduled.
1840                                         setSafeModeAlarm();
1841                                         break;
1842                                     default:
1843                                         logWtf(
1844                                                 "Unknown validation status "
1845                                                         + status
1846                                                         + "; ignoring");
1847                                         break;
1848                                 }
1849                             } /* validationStatusCallback */);
1850 
1851             agent.register();
1852             agent.markConnected();
1853 
1854             return agent;
1855         }
1856 
clearFailedAttemptCounterAndSafeModeAlarm()1857         protected void clearFailedAttemptCounterAndSafeModeAlarm() {
1858             mVcnContext.ensureRunningOnLooperThread();
1859 
1860             // Validated connection, clear failed attempt counter
1861             mFailedAttempts = 0;
1862             cancelSafeModeAlarm();
1863 
1864             mIsInSafeMode = false;
1865             mGatewayStatusCallback.onSafeModeStatusChanged();
1866         }
1867 
applyTransform( int token, @NonNull IpSecTunnelInterface tunnelIface, @NonNull Network underlyingNetwork, @NonNull IpSecTransform transform, int direction)1868         protected void applyTransform(
1869                 int token,
1870                 @NonNull IpSecTunnelInterface tunnelIface,
1871                 @NonNull Network underlyingNetwork,
1872                 @NonNull IpSecTransform transform,
1873                 int direction) {
1874             if (direction != IpSecManager.DIRECTION_IN && direction != IpSecManager.DIRECTION_OUT) {
1875                 logWtf("Applying transform for unexpected direction: " + direction);
1876             }
1877 
1878             try {
1879                 tunnelIface.setUnderlyingNetwork(underlyingNetwork);
1880 
1881                 // Transforms do not need to be persisted; the IkeSession will keep them alive
1882                 mIpSecManager.applyTunnelModeTransform(tunnelIface, direction, transform);
1883 
1884                 // For inbound transforms, additionally allow forwarded traffic to bridge to DUN (as
1885                 // needed)
1886                 final Set<Integer> exposedCaps = mConnectionConfig.getAllExposedCapabilities();
1887                 if (direction == IpSecManager.DIRECTION_IN
1888                         && exposedCaps.contains(NET_CAPABILITY_DUN)) {
1889                     mIpSecManager.applyTunnelModeTransform(
1890                             tunnelIface, IpSecManager.DIRECTION_FWD, transform);
1891                 }
1892             } catch (IOException e) {
1893                 logInfo("Transform application failed for network " + token, e);
1894                 sessionLost(token, e);
1895             }
1896         }
1897 
setupInterface( int token, @NonNull IpSecTunnelInterface tunnelIface, @NonNull VcnChildSessionConfiguration childConfig, @Nullable VcnChildSessionConfiguration oldChildConfig)1898         protected void setupInterface(
1899                 int token,
1900                 @NonNull IpSecTunnelInterface tunnelIface,
1901                 @NonNull VcnChildSessionConfiguration childConfig,
1902                 @Nullable VcnChildSessionConfiguration oldChildConfig) {
1903             try {
1904                 final Set<LinkAddress> newAddrs =
1905                         new ArraySet<>(childConfig.getInternalAddresses());
1906                 final Set<LinkAddress> existingAddrs = new ArraySet<>();
1907                 if (oldChildConfig != null) {
1908                     existingAddrs.addAll(oldChildConfig.getInternalAddresses());
1909                 }
1910 
1911                 final Set<LinkAddress> toAdd = new ArraySet<>();
1912                 toAdd.addAll(newAddrs);
1913                 toAdd.removeAll(existingAddrs);
1914 
1915                 final Set<LinkAddress> toRemove = new ArraySet<>();
1916                 toRemove.addAll(existingAddrs);
1917                 toRemove.removeAll(newAddrs);
1918 
1919                 for (LinkAddress address : toAdd) {
1920                     tunnelIface.addAddress(address.getAddress(), address.getPrefixLength());
1921                 }
1922 
1923                 for (LinkAddress address : toRemove) {
1924                     tunnelIface.removeAddress(address.getAddress(), address.getPrefixLength());
1925                 }
1926             } catch (IOException e) {
1927                 logInfo("Adding address to tunnel failed for token " + token, e);
1928                 sessionLost(token, e);
1929             }
1930         }
1931     }
1932 
1933     /**
1934      * Stable state representing a VCN that has a functioning connection to the mobility anchor.
1935      *
1936      * <p>This state handles IPsec transform application (initial and rekey), NetworkAgent setup,
1937      * and monitors for mobility events.
1938      */
1939     class ConnectedState extends ConnectedStateBase {
1940         @Override
enterState()1941         protected void enterState() throws Exception {
1942             if (mTunnelIface == null) {
1943                 try {
1944                     // Requires a real Network object in order to be created; doing this any earlier
1945                     // means not having a real Network object, or picking an incorrect Network.
1946                     mTunnelIface =
1947                             mIpSecManager.createIpSecTunnelInterface(
1948                                     DUMMY_ADDR, DUMMY_ADDR, mUnderlying.network);
1949                 } catch (IOException | ResourceUnavailableException e) {
1950                     teardownAsynchronously();
1951                 }
1952             }
1953         }
1954 
1955         @Override
processStateMsg(Message msg)1956         protected void processStateMsg(Message msg) {
1957             switch (msg.what) {
1958                 case EVENT_UNDERLYING_NETWORK_CHANGED:
1959                     handleUnderlyingNetworkChanged(msg);
1960                     break;
1961                 case EVENT_SESSION_CLOSED:
1962                     // Disconnecting state waits for EVENT_SESSION_CLOSED to shutdown, and this
1963                     // message may not be posted again. Defer to ensure immediate shutdown.
1964                     deferMessage(msg);
1965                     transitionTo(mDisconnectingState);
1966                     break;
1967                 case EVENT_SESSION_LOST:
1968                     transitionTo(mDisconnectingState);
1969                     break;
1970                 case EVENT_TRANSFORM_CREATED:
1971                     final EventTransformCreatedInfo transformCreatedInfo =
1972                             (EventTransformCreatedInfo) msg.obj;
1973 
1974                     applyTransform(
1975                             mCurrentToken,
1976                             mTunnelIface,
1977                             mUnderlying.network,
1978                             transformCreatedInfo.transform,
1979                             transformCreatedInfo.direction);
1980                     break;
1981                 case EVENT_SETUP_COMPLETED:
1982                     final VcnChildSessionConfiguration oldChildConfig = mChildConfig;
1983                     mChildConfig = ((EventSetupCompletedInfo) msg.obj).childSessionConfig;
1984 
1985                     setupInterfaceAndNetworkAgent(
1986                             mCurrentToken,
1987                             mTunnelIface,
1988                             mChildConfig,
1989                             oldChildConfig,
1990                             mIkeConnectionInfo);
1991 
1992                     // Create opportunistic child SAs; this allows SA aggregation in the downlink,
1993                     // reducing lock/atomic contention in high throughput scenarios. All SAs will
1994                     // share the same UDP encap socket (and keepalives) as necessary, and are
1995                     // effectively free.
1996                     final int parallelTunnelCount =
1997                             mDeps.getParallelTunnelCount(mLastSnapshot, mSubscriptionGroup);
1998                     logInfo("Parallel tunnel count: " + parallelTunnelCount);
1999 
2000                     for (int i = 0; i < parallelTunnelCount - 1; i++) {
2001                         mIkeSession.openChildSession(
2002                                 buildOpportunisticChildParams(),
2003                                 new VcnChildSessionCallback(
2004                                         mCurrentToken, true /* isOpportunistic */));
2005                     }
2006 
2007                     break;
2008                 case EVENT_DISCONNECT_REQUESTED:
2009                     handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj);
2010                     break;
2011                 case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
2012                     handleSafeModeTimeoutExceeded();
2013                     break;
2014                 case EVENT_MIGRATION_COMPLETED:
2015                     final EventMigrationCompletedInfo migrationCompletedInfo =
2016                             (EventMigrationCompletedInfo) msg.obj;
2017 
2018                     handleMigrationCompleted(migrationCompletedInfo);
2019                     break;
2020                 case EVENT_IKE_CONNECTION_INFO_CHANGED:
2021                     mIkeConnectionInfo =
2022                             ((EventIkeConnectionInfoChangedInfo) msg.obj).ikeConnectionInfo;
2023                     break;
2024                 case EVENT_DATA_STALL_SUSPECTED:
2025                     final Network networkWithDataStall =
2026                             ((EventDataStallSuspectedInfo) msg.obj).network;
2027                     handleDataStallSuspected(networkWithDataStall);
2028                     break;
2029                 default:
2030                     logUnhandledMessage(msg);
2031                     break;
2032             }
2033         }
2034 
handleMigrationCompleted(EventMigrationCompletedInfo migrationCompletedInfo)2035         private void handleMigrationCompleted(EventMigrationCompletedInfo migrationCompletedInfo) {
2036             logInfo("Migration completed: " + mUnderlying.network);
2037 
2038             applyTransform(
2039                     mCurrentToken,
2040                     mTunnelIface,
2041                     mUnderlying.network,
2042                     migrationCompletedInfo.inTransform,
2043                     IpSecManager.DIRECTION_IN);
2044 
2045             applyTransform(
2046                     mCurrentToken,
2047                     mTunnelIface,
2048                     mUnderlying.network,
2049                     migrationCompletedInfo.outTransform,
2050                     IpSecManager.DIRECTION_OUT);
2051 
2052             updateNetworkAgent(mTunnelIface, mNetworkAgent, mChildConfig, mIkeConnectionInfo);
2053 
2054             // Trigger re-validation after migration events.
2055             mConnectivityManager.reportNetworkConnectivity(
2056                     mNetworkAgent.getNetwork(), false /* hasConnectivity */);
2057         }
2058 
handleUnderlyingNetworkChanged(@onNull Message msg)2059         private void handleUnderlyingNetworkChanged(@NonNull Message msg) {
2060             final UnderlyingNetworkRecord oldUnderlying = mUnderlying;
2061             mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
2062 
2063             if (mUnderlying == null) {
2064                 logInfo("Underlying network lost");
2065 
2066                 // Ignored for now; a new network may be coming up. If none does, the delayed
2067                 // NETWORK_LOST disconnect will be fired, and tear down the session + network.
2068                 return;
2069             }
2070 
2071             // mUnderlying assumed non-null, given check above.
2072             // If network changed, migrate. Otherwise, update any existing networkAgent.
2073             if (oldUnderlying == null || !oldUnderlying.network.equals(mUnderlying.network)) {
2074                 logInfo("Migrating to new network: " + mUnderlying.network);
2075                 mIkeSession.setNetwork(mUnderlying.network);
2076             } else {
2077                 // oldUnderlying is non-null & underlying network itself has not changed
2078                 // (only network properties were changed).
2079 
2080                 // Network not yet set up, or child not yet connected.
2081                 if (mNetworkAgent != null && mChildConfig != null) {
2082                     // If only network properties changed and agent is active, update properties
2083                     updateNetworkAgent(
2084                             mTunnelIface, mNetworkAgent, mChildConfig, mIkeConnectionInfo);
2085                 }
2086             }
2087         }
2088 
handleDataStallSuspected(Network networkWithDataStall)2089         private void handleDataStallSuspected(Network networkWithDataStall) {
2090             if (mUnderlying != null
2091                     && mNetworkAgent != null
2092                     && mNetworkAgent.getNetwork().equals(networkWithDataStall)) {
2093                 logInfo("Perform Mobility update to recover from suspected data stall");
2094                 mIkeSession.setNetwork(mUnderlying.network);
2095             }
2096         }
2097 
setupInterfaceAndNetworkAgent( int token, @NonNull IpSecTunnelInterface tunnelIface, @NonNull VcnChildSessionConfiguration childConfig, @NonNull VcnChildSessionConfiguration oldChildConfig, @NonNull IkeSessionConnectionInfo ikeConnectionInfo)2098         protected void setupInterfaceAndNetworkAgent(
2099                 int token,
2100                 @NonNull IpSecTunnelInterface tunnelIface,
2101                 @NonNull VcnChildSessionConfiguration childConfig,
2102                 @NonNull VcnChildSessionConfiguration oldChildConfig,
2103                 @NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
2104             setupInterface(token, tunnelIface, childConfig, oldChildConfig);
2105 
2106             if (mNetworkAgent == null) {
2107                 mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig, ikeConnectionInfo);
2108             } else {
2109                 updateNetworkAgent(tunnelIface, mNetworkAgent, childConfig, ikeConnectionInfo);
2110 
2111                 // mNetworkAgent not null, so the VCN Network has already been established. Clear
2112                 // the failed attempt counter and safe mode alarm since this transition is complete.
2113                 clearFailedAttemptCounterAndSafeModeAlarm();
2114             }
2115         }
2116 
2117         @Override
exitState()2118         protected void exitState() {
2119             // Will only set a new alarm if no safe mode alarm is currently scheduled.
2120             setSafeModeAlarm();
2121         }
2122     }
2123 
2124     /**
2125      * Transitive state representing a VCN that failed to establish a connection, and will retry.
2126      *
2127      * <p>This state will be exited upon a new underlying network being found, or timeout expiry.
2128      */
2129     class RetryTimeoutState extends ActiveBaseState {
2130         @Override
enterState()2131         protected void enterState() throws Exception {
2132             // Reset upon entry to ConnectedState
2133             mFailedAttempts++;
2134 
2135             if (mUnderlying == null) {
2136                 logWtf("Underlying network was null in retry state");
2137                 teardownNetwork();
2138                 transitionTo(mDisconnectedState);
2139             } else {
2140                 // Safe to blindly set up, as it is cancelled and cleared on exiting this state
2141                 setRetryTimeoutAlarm(getNextRetryIntervalsMs());
2142             }
2143         }
2144 
2145         @Override
processStateMsg(Message msg)2146         protected void processStateMsg(Message msg) {
2147             switch (msg.what) {
2148                 case EVENT_UNDERLYING_NETWORK_CHANGED:
2149                     final UnderlyingNetworkRecord oldUnderlying = mUnderlying;
2150                     mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
2151 
2152                     // If new underlying is null, all networks were lost; go back to disconnected.
2153                     if (mUnderlying == null) {
2154                         teardownNetwork();
2155                         transitionTo(mDisconnectedState);
2156                         return;
2157                     } else if (oldUnderlying != null
2158                             && mUnderlying.network.equals(oldUnderlying.network)) {
2159                         // If the network has not changed, do nothing.
2160                         return;
2161                     }
2162 
2163                     // Fallthrough
2164                 case EVENT_RETRY_TIMEOUT_EXPIRED:
2165                     transitionTo(mConnectingState);
2166                     break;
2167                 case EVENT_DISCONNECT_REQUESTED:
2168                     handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj);
2169                     break;
2170                 case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
2171                     handleSafeModeTimeoutExceeded();
2172                     break;
2173                 default:
2174                     logUnhandledMessage(msg);
2175                     break;
2176             }
2177         }
2178 
2179         @Override
exitState()2180         public void exitState() {
2181             cancelRetryTimeoutAlarm();
2182         }
2183 
getNextRetryIntervalsMs()2184         private long getNextRetryIntervalsMs() {
2185             final int retryDelayIndex = mFailedAttempts - 1;
2186             final long[] retryIntervalsMs = mConnectionConfig.getRetryIntervalsMillis();
2187 
2188             // Repeatedly use last item in retry timeout list.
2189             if (retryDelayIndex >= retryIntervalsMs.length) {
2190                 return retryIntervalsMs[retryIntervalsMs.length - 1];
2191             }
2192 
2193             return retryIntervalsMs[retryDelayIndex];
2194         }
2195     }
2196 
2197     @VisibleForTesting(visibility = Visibility.PRIVATE)
buildNetworkCapabilities( @onNull VcnGatewayConnectionConfig gatewayConnectionConfig, @Nullable UnderlyingNetworkRecord underlying, boolean isMobileDataEnabled)2198     static NetworkCapabilities buildNetworkCapabilities(
2199             @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig,
2200             @Nullable UnderlyingNetworkRecord underlying,
2201             boolean isMobileDataEnabled) {
2202         final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
2203 
2204         builder.addTransportType(TRANSPORT_CELLULAR);
2205         builder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
2206         builder.addCapability(NET_CAPABILITY_NOT_CONGESTED);
2207         builder.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
2208 
2209         // Add exposed capabilities
2210         for (int cap : gatewayConnectionConfig.getAllExposedCapabilities()) {
2211             // Skip adding INTERNET or DUN if mobile data is disabled.
2212             if (!isMobileDataEnabled
2213                     && (cap == NET_CAPABILITY_INTERNET || cap == NET_CAPABILITY_DUN)) {
2214                 continue;
2215             }
2216 
2217             builder.addCapability(cap);
2218         }
2219 
2220         if (underlying != null) {
2221             final NetworkCapabilities underlyingCaps = underlying.networkCapabilities;
2222 
2223             // Mirror merged capabilities.
2224             for (int cap : MERGED_CAPABILITIES) {
2225                 if (underlyingCaps.hasCapability(cap)) {
2226                     builder.addCapability(cap);
2227                 }
2228             }
2229 
2230             // Set admin UIDs for ConnectivityDiagnostics use.
2231             final int[] underlyingAdminUids = underlyingCaps.getAdministratorUids();
2232             Arrays.sort(underlyingAdminUids); // Sort to allow contains check below.
2233 
2234             int[] adminUids;
2235             if (underlyingCaps.getOwnerUid() > 0 // No owner UID specified
2236                     && 0 > Arrays.binarySearch(// Owner UID not found in admin UID list.
2237                             underlyingAdminUids, underlyingCaps.getOwnerUid())) {
2238                 adminUids = Arrays.copyOf(underlyingAdminUids, underlyingAdminUids.length + 1);
2239                 adminUids[adminUids.length - 1] = underlyingCaps.getOwnerUid();
2240                 Arrays.sort(adminUids);
2241             } else {
2242                 adminUids = underlyingAdminUids;
2243             }
2244 
2245             // Set owner & administrator UID
2246             builder.setOwnerUid(Process.myUid());
2247             adminUids = Arrays.copyOf(adminUids, adminUids.length + 1);
2248             adminUids[adminUids.length - 1] = Process.myUid();
2249             builder.setAdministratorUids(adminUids);
2250 
2251             builder.setLinkUpstreamBandwidthKbps(underlyingCaps.getLinkUpstreamBandwidthKbps());
2252             builder.setLinkDownstreamBandwidthKbps(underlyingCaps.getLinkDownstreamBandwidthKbps());
2253 
2254             // Set TransportInfo for SysUI use (never parcelled out of SystemServer).
2255             if (underlyingCaps.hasTransport(TRANSPORT_WIFI)
2256                     && underlyingCaps.getTransportInfo() instanceof WifiInfo) {
2257                 final WifiInfo wifiInfo = (WifiInfo) underlyingCaps.getTransportInfo();
2258                 builder.setTransportInfo(
2259                         new VcnTransportInfo(
2260                                 wifiInfo,
2261                                 gatewayConnectionConfig.getMinUdpPort4500NatTimeoutSeconds()));
2262             } else if (underlyingCaps.hasTransport(TRANSPORT_CELLULAR)
2263                     && underlyingCaps.getNetworkSpecifier() instanceof TelephonyNetworkSpecifier) {
2264                 final TelephonyNetworkSpecifier telNetSpecifier =
2265                         (TelephonyNetworkSpecifier) underlyingCaps.getNetworkSpecifier();
2266                 builder.setTransportInfo(
2267                         new VcnTransportInfo(
2268                                 telNetSpecifier.getSubscriptionId(),
2269                                 gatewayConnectionConfig.getMinUdpPort4500NatTimeoutSeconds()));
2270             } else {
2271                 Slog.wtf(
2272                         TAG,
2273                         "Unknown transport type or missing TransportInfo/NetworkSpecifier for"
2274                                 + " non-null underlying network");
2275             }
2276             builder.setUnderlyingNetworks(List.of(underlying.network));
2277         } else {
2278             Slog.wtf(
2279                     TAG,
2280                     "No underlying network while building network capabilities",
2281                     new IllegalStateException());
2282         }
2283 
2284         return builder.build();
2285     }
2286 
2287     @VisibleForTesting(visibility = Visibility.PRIVATE)
buildConnectedLinkProperties( @onNull VcnGatewayConnectionConfig gatewayConnectionConfig, @NonNull IpSecTunnelInterface tunnelIface, @NonNull VcnChildSessionConfiguration childConfig, @Nullable UnderlyingNetworkRecord underlying, @NonNull IkeSessionConnectionInfo ikeConnectionInfo)2288     LinkProperties buildConnectedLinkProperties(
2289             @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig,
2290             @NonNull IpSecTunnelInterface tunnelIface,
2291             @NonNull VcnChildSessionConfiguration childConfig,
2292             @Nullable UnderlyingNetworkRecord underlying,
2293             @NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
2294         final IkeTunnelConnectionParams ikeTunnelParams =
2295                 gatewayConnectionConfig.getTunnelConnectionParams();
2296         final LinkProperties lp = new LinkProperties();
2297 
2298         lp.setInterfaceName(tunnelIface.getInterfaceName());
2299         for (LinkAddress addr : childConfig.getInternalAddresses()) {
2300             lp.addLinkAddress(addr);
2301         }
2302         for (InetAddress addr : childConfig.getInternalDnsServers()) {
2303             lp.addDnsServer(addr);
2304         }
2305 
2306         lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null /*gateway*/,
2307                 null /*iface*/, RouteInfo.RTN_UNICAST));
2308         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null /*gateway*/,
2309                 null /*iface*/, RouteInfo.RTN_UNICAST));
2310 
2311         int underlyingMtu = 0;
2312         if (underlying != null) {
2313             final LinkProperties underlyingLp = underlying.linkProperties;
2314 
2315             lp.setTcpBufferSizes(underlyingLp.getTcpBufferSizes());
2316             underlyingMtu = underlyingLp.getMtu();
2317 
2318             // WiFi LinkProperties uses DHCP as the sole source of MTU information, and as a result
2319             // often lists MTU as 0 (see b/184678973). Use the interface MTU as retrieved by
2320             // NetworkInterface APIs.
2321             if (underlyingMtu == 0 && underlyingLp.getInterfaceName() != null) {
2322                 underlyingMtu = mDeps.getUnderlyingIfaceMtu(underlyingLp.getInterfaceName());
2323             }
2324         } else {
2325             Slog.wtf(
2326                     TAG,
2327                     "No underlying network while building link properties",
2328                     new IllegalStateException());
2329         }
2330         lp.setMtu(
2331                 MtuUtils.getMtu(
2332                         ikeTunnelParams.getTunnelModeChildSessionParams().getSaProposals(),
2333                         gatewayConnectionConfig.getMaxMtu(),
2334                         underlyingMtu,
2335                         ikeConnectionInfo.getLocalAddress() instanceof Inet4Address));
2336 
2337         return lp;
2338     }
2339 
2340     private class IkeSessionCallbackImpl implements IkeSessionCallback {
2341         private final int mToken;
2342 
IkeSessionCallbackImpl(int token)2343         IkeSessionCallbackImpl(int token) {
2344             mToken = token;
2345         }
2346 
2347         @Override
onOpened(@onNull IkeSessionConfiguration ikeSessionConfig)2348         public void onOpened(@NonNull IkeSessionConfiguration ikeSessionConfig) {
2349             logDbg("IkeOpened for token " + mToken);
2350             ikeConnectionInfoChanged(mToken, ikeSessionConfig.getIkeSessionConnectionInfo());
2351         }
2352 
2353         @Override
onClosed()2354         public void onClosed() {
2355             logDbg("IkeClosed for token " + mToken);
2356             sessionClosed(mToken, null);
2357         }
2358 
2359         @Override
onClosedExceptionally(@onNull IkeException exception)2360         public void onClosedExceptionally(@NonNull IkeException exception) {
2361             logInfo("IkeClosedExceptionally for token " + mToken, exception);
2362             sessionClosed(mToken, exception);
2363         }
2364 
2365         @Override
onError(@onNull IkeProtocolException exception)2366         public void onError(@NonNull IkeProtocolException exception) {
2367             logInfo("IkeError for token " + mToken, exception);
2368             // Non-fatal, log and continue.
2369         }
2370 
2371         @Override
onIkeSessionConnectionInfoChanged( @onNull IkeSessionConnectionInfo connectionInfo)2372         public void onIkeSessionConnectionInfoChanged(
2373                 @NonNull IkeSessionConnectionInfo connectionInfo) {
2374             logDbg("onIkeSessionConnectionInfoChanged for token " + mToken);
2375             ikeConnectionInfoChanged(mToken, connectionInfo);
2376         }
2377     }
2378 
2379     /** Implementation of ChildSessionCallback, exposed for testing. */
2380     @VisibleForTesting(visibility = Visibility.PRIVATE)
2381     public class VcnChildSessionCallback implements ChildSessionCallback {
2382         private final int mToken;
2383         private final boolean mIsOpportunistic;
2384 
2385         private boolean mIsChildOpened = false;
2386 
VcnChildSessionCallback(int token)2387         VcnChildSessionCallback(int token) {
2388             this(token, false /* isOpportunistic */);
2389         }
2390 
2391         /**
2392          * Creates a ChildSessionCallback
2393          *
2394          * <p>If configured as opportunistic, transforms will not report initial startup, or
2395          * associated startup failures. This serves the dual purposes of ensuring that if the server
2396          * does not support connection multiplexing, new child SA negotiations will be ignored, and
2397          * at the same time, will notify the VCN session if a successfully negotiated opportunistic
2398          * child SA is subsequently torn down, which could impact uplink traffic if the SA in use
2399          * for outbound/uplink traffic is this opportunistic SA.
2400          *
2401          * <p>While inbound SAs can be used in parallel, the IPsec stack explicitly selects the last
2402          * applied outbound transform for outbound traffic. This means that unlike inbound traffic,
2403          * outbound does not benefit from these parallel SAs in the same manner.
2404          */
VcnChildSessionCallback(int token, boolean isOpportunistic)2405         VcnChildSessionCallback(int token, boolean isOpportunistic) {
2406             mToken = token;
2407             mIsOpportunistic = isOpportunistic;
2408         }
2409 
2410         /** Internal proxy method for injecting of mocked ChildSessionConfiguration */
2411         @VisibleForTesting(visibility = Visibility.PRIVATE)
onOpened(@onNull VcnChildSessionConfiguration childConfig)2412         void onOpened(@NonNull VcnChildSessionConfiguration childConfig) {
2413             logDbg("ChildOpened for token " + mToken);
2414 
2415             if (mIsOpportunistic) {
2416                 logDbg("ChildOpened for opportunistic child; suppressing event message");
2417                 mIsChildOpened = true;
2418                 return;
2419             }
2420 
2421             childOpened(mToken, childConfig);
2422         }
2423 
2424         @Override
onOpened(@onNull ChildSessionConfiguration childConfig)2425         public void onOpened(@NonNull ChildSessionConfiguration childConfig) {
2426             onOpened(new VcnChildSessionConfiguration(childConfig));
2427         }
2428 
2429         @Override
onClosed()2430         public void onClosed() {
2431             logDbg("ChildClosed for token " + mToken);
2432 
2433             if (mIsOpportunistic && !mIsChildOpened) {
2434                 logDbg("ChildClosed for unopened opportunistic child; ignoring");
2435                 return;
2436             }
2437 
2438             sessionLost(mToken, null);
2439         }
2440 
2441         @Override
onClosedExceptionally(@onNull IkeException exception)2442         public void onClosedExceptionally(@NonNull IkeException exception) {
2443             logInfo("ChildClosedExceptionally for token " + mToken, exception);
2444 
2445             if (mIsOpportunistic && !mIsChildOpened) {
2446                 logInfo("ChildClosedExceptionally for unopened opportunistic child; ignoring");
2447                 return;
2448             }
2449 
2450             sessionLost(mToken, exception);
2451         }
2452 
2453         @Override
onIpSecTransformCreated(@onNull IpSecTransform transform, int direction)2454         public void onIpSecTransformCreated(@NonNull IpSecTransform transform, int direction) {
2455             logDbg("ChildTransformCreated; Direction: " + direction + "; token " + mToken);
2456             childTransformCreated(mToken, transform, direction);
2457         }
2458 
2459         @Override
onIpSecTransformsMigrated( @onNull IpSecTransform inIpSecTransform, @NonNull IpSecTransform outIpSecTransform)2460         public void onIpSecTransformsMigrated(
2461                 @NonNull IpSecTransform inIpSecTransform,
2462                 @NonNull IpSecTransform outIpSecTransform) {
2463             logDbg("ChildTransformsMigrated; token " + mToken);
2464             migrationCompleted(mToken, inIpSecTransform, outIpSecTransform);
2465         }
2466 
2467         @Override
onIpSecTransformDeleted(@onNull IpSecTransform transform, int direction)2468         public void onIpSecTransformDeleted(@NonNull IpSecTransform transform, int direction) {
2469             // Nothing to be done; no references to the IpSecTransform are held, and this transform
2470             // will be closed by the IKE library.
2471             logDbg("ChildTransformDeleted; Direction: " + direction + "; for token " + mToken);
2472         }
2473     }
2474 
2475     // Used in Vcn.java, but must be public for mockito to mock this.
getLogPrefix()2476     public String getLogPrefix() {
2477         return "("
2478                 + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup)
2479                 + "-"
2480                 + mConnectionConfig.getGatewayConnectionName()
2481                 + "-"
2482                 + System.identityHashCode(this)
2483                 + ") ";
2484     }
2485 
getTagLogPrefix()2486     private String getTagLogPrefix() {
2487         return "[ " + TAG + " " + getLogPrefix() + "]";
2488     }
2489 
logVdbg(String msg)2490     private void logVdbg(String msg) {
2491         if (VDBG) {
2492             Slog.v(TAG, getLogPrefix() + msg);
2493         }
2494     }
2495 
logDbg(String msg)2496     private void logDbg(String msg) {
2497         Slog.d(TAG, getLogPrefix() + msg);
2498     }
2499 
logDbg(String msg, Throwable tr)2500     private void logDbg(String msg, Throwable tr) {
2501         Slog.d(TAG, getLogPrefix() + msg, tr);
2502     }
2503 
logInfo(String msg)2504     private void logInfo(String msg) {
2505         Slog.i(TAG, getLogPrefix() + msg);
2506         LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg);
2507     }
2508 
logInfo(String msg, Throwable tr)2509     private void logInfo(String msg, Throwable tr) {
2510         Slog.i(TAG, getLogPrefix() + msg, tr);
2511         LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg + tr);
2512     }
2513 
logWarn(String msg)2514     private void logWarn(String msg) {
2515         Slog.w(TAG, getLogPrefix() + msg);
2516         LOCAL_LOG.log("[WARN] " + getTagLogPrefix() + msg);
2517     }
2518 
logWarn(String msg, Throwable tr)2519     private void logWarn(String msg, Throwable tr) {
2520         Slog.w(TAG, getLogPrefix() + msg, tr);
2521         LOCAL_LOG.log("[WARN] " + getTagLogPrefix() + msg + tr);
2522     }
2523 
logErr(String msg)2524     private void logErr(String msg) {
2525         Slog.e(TAG, getLogPrefix() + msg);
2526         LOCAL_LOG.log("[ERR ] " + getTagLogPrefix() + msg);
2527     }
2528 
logErr(String msg, Throwable tr)2529     private void logErr(String msg, Throwable tr) {
2530         Slog.e(TAG, getLogPrefix() + msg, tr);
2531         LOCAL_LOG.log("[ERR ] " + getTagLogPrefix() + msg + tr);
2532     }
2533 
logWtf(String msg)2534     private void logWtf(String msg) {
2535         Slog.wtf(TAG, getLogPrefix() + msg);
2536         LOCAL_LOG.log("[WTF ] " + msg);
2537     }
2538 
logWtf(String msg, Throwable tr)2539     private void logWtf(String msg, Throwable tr) {
2540         Slog.wtf(TAG, getLogPrefix() + msg, tr);
2541         LOCAL_LOG.log("[WTF ] " + msg + tr);
2542     }
2543 
2544     /**
2545      * Dumps the state of this VcnGatewayConnection for logging and debugging purposes.
2546      *
2547      * <p>PII and credentials MUST NEVER be dumped here.
2548      */
dump(IndentingPrintWriter pw)2549     public void dump(IndentingPrintWriter pw) {
2550         pw.println("VcnGatewayConnection (" + mConnectionConfig.getGatewayConnectionName() + "):");
2551         pw.increaseIndent();
2552 
2553         pw.println(
2554                 "Current state: "
2555                         + (getCurrentState() == null
2556                                 ? null
2557                                 : getCurrentState().getClass().getSimpleName()));
2558         pw.println("mIsQuitting: " + mIsQuitting.getValue());
2559         pw.println("mIsInSafeMode: " + mIsInSafeMode);
2560         pw.println("mCurrentToken: " + mCurrentToken);
2561         pw.println("mFailedAttempts: " + mFailedAttempts);
2562         pw.println(
2563                 "mNetworkAgent.getNetwork(): "
2564                         + (mNetworkAgent == null ? null : mNetworkAgent.getNetwork()));
2565         pw.println();
2566 
2567         mUnderlyingNetworkController.dump(pw);
2568         pw.println();
2569 
2570         pw.decreaseIndent();
2571     }
2572 
2573     @VisibleForTesting(visibility = Visibility.PRIVATE)
setTunnelInterface(IpSecTunnelInterface tunnelIface)2574     void setTunnelInterface(IpSecTunnelInterface tunnelIface) {
2575         mTunnelIface = tunnelIface;
2576     }
2577 
2578     @VisibleForTesting(visibility = Visibility.PRIVATE)
getUnderlyingNetworkControllerCallback()2579     UnderlyingNetworkControllerCallback getUnderlyingNetworkControllerCallback() {
2580         return mUnderlyingNetworkControllerCallback;
2581     }
2582 
2583     @VisibleForTesting(visibility = Visibility.PRIVATE)
getConnectivityDiagnosticsCallback()2584     ConnectivityDiagnosticsCallback getConnectivityDiagnosticsCallback() {
2585         return mConnectivityDiagnosticsCallback;
2586     }
2587 
2588     @VisibleForTesting(visibility = Visibility.PRIVATE)
getUnderlyingNetwork()2589     UnderlyingNetworkRecord getUnderlyingNetwork() {
2590         return mUnderlying;
2591     }
2592 
2593     @VisibleForTesting(visibility = Visibility.PRIVATE)
setUnderlyingNetwork(@ullable UnderlyingNetworkRecord record)2594     void setUnderlyingNetwork(@Nullable UnderlyingNetworkRecord record) {
2595         mUnderlying = record;
2596     }
2597 
2598     @VisibleForTesting(visibility = Visibility.PRIVATE)
getIkeConnectionInfo()2599     IkeSessionConnectionInfo getIkeConnectionInfo() {
2600         return mIkeConnectionInfo;
2601     }
2602 
2603     @VisibleForTesting(visibility = Visibility.PRIVATE)
isQuitting()2604     boolean isQuitting() {
2605         return mIsQuitting.getValue();
2606     }
2607 
2608     @VisibleForTesting(visibility = Visibility.PRIVATE)
setQuitting()2609     void setQuitting() {
2610         mIsQuitting.setTrue();
2611     }
2612 
2613     @VisibleForTesting(visibility = Visibility.PRIVATE)
getIkeSession()2614     VcnIkeSession getIkeSession() {
2615         return mIkeSession;
2616     }
2617 
2618     @VisibleForTesting(visibility = Visibility.PRIVATE)
setIkeSession(@ullable VcnIkeSession session)2619     void setIkeSession(@Nullable VcnIkeSession session) {
2620         mIkeSession = session;
2621     }
2622 
2623     @VisibleForTesting(visibility = Visibility.PRIVATE)
getNetworkAgent()2624     VcnNetworkAgent getNetworkAgent() {
2625         return mNetworkAgent;
2626     }
2627 
2628     @VisibleForTesting(visibility = Visibility.PRIVATE)
setNetworkAgent(@ullable VcnNetworkAgent networkAgent)2629     void setNetworkAgent(@Nullable VcnNetworkAgent networkAgent) {
2630         mNetworkAgent = networkAgent;
2631     }
2632 
2633     @VisibleForTesting(visibility = Visibility.PRIVATE)
sendDisconnectRequestedAndAcquireWakelock(String reason, boolean shouldQuit)2634     void sendDisconnectRequestedAndAcquireWakelock(String reason, boolean shouldQuit) {
2635         sendMessageAndAcquireWakeLock(
2636                 EVENT_DISCONNECT_REQUESTED,
2637                 TOKEN_ALL,
2638                 new EventDisconnectRequestedInfo(reason, shouldQuit));
2639     }
2640 
buildIkeParams(@onNull Network network)2641     private IkeSessionParams buildIkeParams(@NonNull Network network) {
2642         final IkeTunnelConnectionParams ikeTunnelConnectionParams =
2643                 mConnectionConfig.getTunnelConnectionParams();
2644         final IkeSessionParams.Builder builder =
2645                 new IkeSessionParams.Builder(ikeTunnelConnectionParams.getIkeSessionParams());
2646         builder.setNetwork(network);
2647         return builder.build();
2648     }
2649 
buildChildParams()2650     private ChildSessionParams buildChildParams() {
2651         return mConnectionConfig.getTunnelConnectionParams().getTunnelModeChildSessionParams();
2652     }
2653 
buildOpportunisticChildParams()2654     private ChildSessionParams buildOpportunisticChildParams() {
2655         final ChildSessionParams baseParams =
2656                 mConnectionConfig.getTunnelConnectionParams().getTunnelModeChildSessionParams();
2657 
2658         final TunnelModeChildSessionParams.Builder builder =
2659                 new TunnelModeChildSessionParams.Builder();
2660         for (ChildSaProposal proposal : baseParams.getChildSaProposals()) {
2661             builder.addChildSaProposal(proposal);
2662         }
2663 
2664         for (IkeTrafficSelector inboundSelector : baseParams.getInboundTrafficSelectors()) {
2665             builder.addInboundTrafficSelectors(inboundSelector);
2666         }
2667 
2668         for (IkeTrafficSelector outboundSelector : baseParams.getOutboundTrafficSelectors()) {
2669             builder.addOutboundTrafficSelectors(outboundSelector);
2670         }
2671 
2672         builder.setLifetimeSeconds(
2673                 baseParams.getHardLifetimeSeconds(), baseParams.getSoftLifetimeSeconds());
2674 
2675         return builder.build();
2676     }
2677 
2678     @VisibleForTesting(visibility = Visibility.PRIVATE)
buildIkeSession(@onNull Network network)2679     VcnIkeSession buildIkeSession(@NonNull Network network) {
2680         final int token = ++mCurrentToken;
2681 
2682         return mDeps.newIkeSession(
2683                 mVcnContext,
2684                 buildIkeParams(network),
2685                 buildChildParams(),
2686                 new IkeSessionCallbackImpl(token),
2687                 new VcnChildSessionCallback(token));
2688     }
2689 
2690     /** External dependencies used by VcnGatewayConnection, for injection in tests */
2691     @VisibleForTesting(visibility = Visibility.PRIVATE)
2692     public static class Dependencies {
2693         /** Builds a new UnderlyingNetworkController. */
newUnderlyingNetworkController( VcnContext vcnContext, VcnGatewayConnectionConfig connectionConfig, ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, UnderlyingNetworkControllerCallback callback)2694         public UnderlyingNetworkController newUnderlyingNetworkController(
2695                 VcnContext vcnContext,
2696                 VcnGatewayConnectionConfig connectionConfig,
2697                 ParcelUuid subscriptionGroup,
2698                 TelephonySubscriptionSnapshot snapshot,
2699                 UnderlyingNetworkControllerCallback callback) {
2700             return new UnderlyingNetworkController(
2701                     vcnContext, connectionConfig, subscriptionGroup, snapshot, callback);
2702         }
2703 
2704         /** Builds a new IkeSession. */
newIkeSession( VcnContext vcnContext, IkeSessionParams ikeSessionParams, ChildSessionParams childSessionParams, IkeSessionCallback ikeSessionCallback, ChildSessionCallback childSessionCallback)2705         public VcnIkeSession newIkeSession(
2706                 VcnContext vcnContext,
2707                 IkeSessionParams ikeSessionParams,
2708                 ChildSessionParams childSessionParams,
2709                 IkeSessionCallback ikeSessionCallback,
2710                 ChildSessionCallback childSessionCallback) {
2711             return new VcnIkeSession(
2712                     vcnContext,
2713                     ikeSessionParams,
2714                     childSessionParams,
2715                     ikeSessionCallback,
2716                     childSessionCallback);
2717         }
2718 
2719         /** Builds a new WakeLock. */
newWakeLock( @onNull Context context, int wakeLockFlag, @NonNull String wakeLockTag)2720         public VcnWakeLock newWakeLock(
2721                 @NonNull Context context, int wakeLockFlag, @NonNull String wakeLockTag) {
2722             return new VcnWakeLock(context, wakeLockFlag, wakeLockTag);
2723         }
2724 
2725         /** Builds a new WakeupMessage. */
newWakeupMessage( @onNull VcnContext vcnContext, @NonNull Handler handler, @NonNull String tag, @NonNull Runnable runnable)2726         public WakeupMessage newWakeupMessage(
2727                 @NonNull VcnContext vcnContext,
2728                 @NonNull Handler handler,
2729                 @NonNull String tag,
2730                 @NonNull Runnable runnable) {
2731             return new WakeupMessage(vcnContext.getContext(), handler, tag, runnable);
2732         }
2733 
2734         /** 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)2735         public VcnNetworkAgent newNetworkAgent(
2736                 @NonNull VcnContext vcnContext,
2737                 @NonNull String tag,
2738                 @NonNull NetworkCapabilities caps,
2739                 @NonNull LinkProperties lp,
2740                 @NonNull NetworkScore score,
2741                 @NonNull NetworkAgentConfig nac,
2742                 @NonNull NetworkProvider provider,
2743                 @NonNull Consumer<VcnNetworkAgent> networkUnwantedCallback,
2744                 @NonNull Consumer<Integer> validationStatusCallback) {
2745             return new VcnNetworkAgent(
2746                     vcnContext,
2747                     tag,
2748                     caps,
2749                     lp,
2750                     score,
2751                     nac,
2752                     provider,
2753                     networkUnwantedCallback,
2754                     validationStatusCallback);
2755         }
2756 
2757         /** Checks if airplane mode is enabled. */
isAirplaneModeOn(@onNull VcnContext vcnContext)2758         public boolean isAirplaneModeOn(@NonNull VcnContext vcnContext) {
2759             return Settings.Global.getInt(vcnContext.getContext().getContentResolver(),
2760                     Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
2761         }
2762 
2763         /** Gets the elapsed real time since boot, in millis. */
getElapsedRealTime()2764         public long getElapsedRealTime() {
2765             return SystemClock.elapsedRealtime();
2766         }
2767 
2768         /** Gets the MTU for the given underlying interface. */
getUnderlyingIfaceMtu(String ifaceName)2769         public int getUnderlyingIfaceMtu(String ifaceName) {
2770             try {
2771                 final NetworkInterface underlyingIface = NetworkInterface.getByName(ifaceName);
2772                 return underlyingIface == null ? 0 : underlyingIface.getMTU();
2773             } catch (IOException e) {
2774                 Slog.d(TAG, "Could not get MTU of underlying network", e);
2775                 return 0;
2776             }
2777         }
2778 
2779         /** Gets the max number of parallel tunnels allowed for tunnel aggregation. */
getParallelTunnelCount( TelephonySubscriptionSnapshot snapshot, ParcelUuid subGrp)2780         public int getParallelTunnelCount(
2781                 TelephonySubscriptionSnapshot snapshot, ParcelUuid subGrp) {
2782             PersistableBundleWrapper carrierConfig = snapshot.getCarrierConfigForSubGrp(subGrp);
2783             int result = TUNNEL_AGGREGATION_SA_COUNT_MAX_DEFAULT;
2784 
2785             if (carrierConfig != null) {
2786                 result =
2787                         carrierConfig.getInt(
2788                                 VcnManager.VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY,
2789                                 TUNNEL_AGGREGATION_SA_COUNT_MAX_DEFAULT);
2790             }
2791 
2792             // Guard against tunnel count < 1
2793             return Math.max(1, result);
2794         }
2795     }
2796 
2797     /**
2798      * Proxy implementation of Child Session Configuration, used for testing.
2799      *
2800      * <p>This wrapper allows mocking of the final, parcelable ChildSessionConfiguration object for
2801      * testing purposes. This is the unfortunate result of mockito-inline (for mocking final
2802      * classes) not working properly with system services & associated classes.
2803      *
2804      * <p>This class MUST EXCLUSIVELY be a passthrough, proxying calls directly to the actual
2805      * ChildSessionConfiguration.
2806      */
2807     @VisibleForTesting(visibility = Visibility.PRIVATE)
2808     public static class VcnChildSessionConfiguration {
2809         private final ChildSessionConfiguration mChildConfig;
2810 
VcnChildSessionConfiguration(ChildSessionConfiguration childConfig)2811         public VcnChildSessionConfiguration(ChildSessionConfiguration childConfig) {
2812             mChildConfig = childConfig;
2813         }
2814 
2815         /** Retrieves the addresses to be used inside the tunnel. */
getInternalAddresses()2816         public List<LinkAddress> getInternalAddresses() {
2817             return mChildConfig.getInternalAddresses();
2818         }
2819 
2820         /** Retrieves the DNS servers to be used inside the tunnel. */
getInternalDnsServers()2821         public List<InetAddress> getInternalDnsServers() {
2822             return mChildConfig.getInternalDnsServers();
2823         }
2824     }
2825 
2826     /** Proxy implementation of IKE session, used for testing. */
2827     @VisibleForTesting(visibility = Visibility.PRIVATE)
2828     public static class VcnIkeSession {
2829         private final IkeSession mImpl;
2830 
VcnIkeSession( VcnContext vcnContext, IkeSessionParams ikeSessionParams, ChildSessionParams childSessionParams, IkeSessionCallback ikeSessionCallback, ChildSessionCallback childSessionCallback)2831         public VcnIkeSession(
2832                 VcnContext vcnContext,
2833                 IkeSessionParams ikeSessionParams,
2834                 ChildSessionParams childSessionParams,
2835                 IkeSessionCallback ikeSessionCallback,
2836                 ChildSessionCallback childSessionCallback) {
2837             mImpl =
2838                     new IkeSession(
2839                             vcnContext.getContext(),
2840                             ikeSessionParams,
2841                             childSessionParams,
2842                             new HandlerExecutor(new Handler(vcnContext.getLooper())),
2843                             ikeSessionCallback,
2844                             childSessionCallback);
2845         }
2846 
2847         /** Creates a new IKE Child session. */
openChildSession( @onNull ChildSessionParams childSessionParams, @NonNull ChildSessionCallback childSessionCallback)2848         public void openChildSession(
2849                 @NonNull ChildSessionParams childSessionParams,
2850                 @NonNull ChildSessionCallback childSessionCallback) {
2851             mImpl.openChildSession(childSessionParams, childSessionCallback);
2852         }
2853 
2854         /** Closes an IKE session as identified by the ChildSessionCallback. */
closeChildSession(@onNull ChildSessionCallback childSessionCallback)2855         public void closeChildSession(@NonNull ChildSessionCallback childSessionCallback) {
2856             mImpl.closeChildSession(childSessionCallback);
2857         }
2858 
2859         /** Gracefully closes this IKE Session, waiting for remote acknowledgement. */
close()2860         public void close() {
2861             mImpl.close();
2862         }
2863 
2864         /** Forcibly kills this IKE Session, without waiting for a closure confirmation. */
kill()2865         public void kill() {
2866             mImpl.kill();
2867         }
2868 
2869         /** Sets the underlying network used by the IkeSession. */
setNetwork(@onNull Network network)2870         public void setNetwork(@NonNull Network network) {
2871             mImpl.setNetwork(network);
2872         }
2873     }
2874 
2875     /** Proxy Implementation of WakeLock, used for testing. */
2876     @VisibleForTesting(visibility = Visibility.PRIVATE)
2877     public static class VcnWakeLock {
2878         private final WakeLock mImpl;
2879 
VcnWakeLock(@onNull Context context, int flags, @NonNull String tag)2880         public VcnWakeLock(@NonNull Context context, int flags, @NonNull String tag) {
2881             final PowerManager powerManager = context.getSystemService(PowerManager.class);
2882             mImpl = powerManager.newWakeLock(flags, tag);
2883             mImpl.setReferenceCounted(false /* isReferenceCounted */);
2884         }
2885 
2886         /**
2887          * Acquire this WakeLock.
2888          *
2889          * <p>Synchronize this action to minimize locking around WakeLock use.
2890          */
acquire()2891         public synchronized void acquire() {
2892             mImpl.acquire();
2893         }
2894 
2895         /**
2896          * Release this Wakelock.
2897          *
2898          * <p>Synchronize this action to minimize locking around WakeLock use.
2899          */
release()2900         public synchronized void release() {
2901             mImpl.release();
2902         }
2903     }
2904 
2905     /** Proxy Implementation of NetworkAgent, used for testing. */
2906     @VisibleForTesting(visibility = Visibility.PRIVATE)
2907     public static class VcnNetworkAgent {
2908         private final NetworkAgent mImpl;
2909 
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)2910         public VcnNetworkAgent(
2911                 @NonNull VcnContext vcnContext,
2912                 @NonNull String tag,
2913                 @NonNull NetworkCapabilities caps,
2914                 @NonNull LinkProperties lp,
2915                 @NonNull NetworkScore score,
2916                 @NonNull NetworkAgentConfig nac,
2917                 @NonNull NetworkProvider provider,
2918                 @NonNull Consumer<VcnNetworkAgent> networkUnwantedCallback,
2919                 @NonNull Consumer<Integer> validationStatusCallback) {
2920             mImpl =
2921                     new NetworkAgent(
2922                             vcnContext.getContext(),
2923                             vcnContext.getLooper(),
2924                             tag,
2925                             caps,
2926                             lp,
2927                             score,
2928                             nac,
2929                             provider) {
2930                         @Override
2931                         public void onNetworkUnwanted() {
2932                             networkUnwantedCallback.accept(VcnNetworkAgent.this);
2933                         }
2934 
2935                         @Override
2936                         public void onValidationStatus(int status, @Nullable Uri redirectUri) {
2937                             validationStatusCallback.accept(status);
2938                         }
2939                     };
2940         }
2941 
2942         /** Registers the underlying NetworkAgent */
register()2943         public void register() {
2944             mImpl.register();
2945         }
2946 
2947         /** Marks the underlying NetworkAgent as connected */
markConnected()2948         public void markConnected() {
2949             mImpl.markConnected();
2950         }
2951 
2952         /** Unregisters the underlying NetworkAgent */
unregister()2953         public void unregister() {
2954             mImpl.unregister();
2955         }
2956 
2957         /** Sends new NetworkCapabilities for the underlying NetworkAgent */
sendNetworkCapabilities(@onNull NetworkCapabilities caps)2958         public void sendNetworkCapabilities(@NonNull NetworkCapabilities caps) {
2959             mImpl.sendNetworkCapabilities(caps);
2960         }
2961 
2962         /** Sends new LinkProperties for the underlying NetworkAgent */
sendLinkProperties(@onNull LinkProperties lp)2963         public void sendLinkProperties(@NonNull LinkProperties lp) {
2964             mImpl.sendLinkProperties(lp);
2965         }
2966 
2967         /** Sends new NetworkCapabilities for the underlying NetworkAgent */
setUnderlyingNetworks(@ullable List<Network> underlyingNetworks)2968         public void setUnderlyingNetworks(@Nullable List<Network> underlyingNetworks) {
2969             mImpl.setUnderlyingNetworks(underlyingNetworks);
2970         }
2971 
2972         /** Retrieves the Network for the underlying NetworkAgent */
2973         @Nullable
getNetwork()2974         public Network getNetwork() {
2975             return mImpl.getNetwork();
2976         }
2977     }
2978 }
2979