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