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