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