diff options
| author | 2021-04-06 01:19:23 +0000 | |
|---|---|---|
| committer | 2021-04-06 01:19:23 +0000 | |
| commit | e253ce3463d4637cec1c281a6d5d7f83d34d3f6c (patch) | |
| tree | 4e2dcbac1612c59b0cd18b40ab30958eb0942837 | |
| parent | caa4617084a7250ad8ac89b8d74de03e6a75689c (diff) | |
| parent | 0906d6bc1b339096149323e9d781a5fe4e591569 (diff) | |
Merge changes Icf32cb46,I43434c80,I59dc46dc,I7df3f14b,I50f2c0e9
* changes:
Don't process dup unwanted() when unregistering NetworkAgent
Add/remove internal addresses from IpSecTunnelInterface
Populate legacy type in VCN NetworkAgentConfig
Add TRANSPORT_CELLULAR to VCN filter
Allow soft-start and opportunistic safe mode
10 files changed, 410 insertions, 241 deletions
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 46f4b0b41a45..f7ae58ca0eb4 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -542,16 +542,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { if (mVcns.containsKey(subscriptionGroup)) { final Vcn vcn = mVcns.get(subscriptionGroup); - final int status = vcn.getStatus(); vcn.updateConfig(config); - - // TODO(b/183174340): Remove this once opportunistic-safe-mode is supported - // Only notify VcnStatusCallbacks if this VCN was previously in Safe Mode - if (status == VCN_STATUS_CODE_SAFE_MODE) { - // TODO(b/181789060): invoke asynchronously after Vcn notifies through VcnCallback - notifyAllPermissionedStatusCallbacksLocked( - subscriptionGroup, VCN_STATUS_CODE_ACTIVE); - } } else { startVcnLocked(subscriptionGroup, config); } @@ -941,8 +932,8 @@ public class VcnManagementService extends IVcnManagementService.Stub { // TODO(b/180452282): Make name more generic and implement directly with VcnManagementService /** Callback for Vcn signals sent up to VcnManagementService. */ public interface VcnCallback { - /** Called by a Vcn to signal that it has entered safe mode. */ - void onEnteredSafeMode(); + /** Called by a Vcn to signal that its safe mode status has changed. */ + void onSafeModeStatusChanged(boolean isInSafeMode); /** Called by a Vcn to signal that an error occurred. */ void onGatewayConnectionError( @@ -1004,15 +995,18 @@ public class VcnManagementService extends IVcnManagementService.Stub { } @Override - public void onEnteredSafeMode() { + public void onSafeModeStatusChanged(boolean isInSafeMode) { synchronized (mLock) { // Ignore if this subscription group doesn't exist anymore if (!mVcns.containsKey(mSubGroup)) { return; } + final int status = + isInSafeMode ? VCN_STATUS_CODE_SAFE_MODE : VCN_STATUS_CODE_ACTIVE; + notifyAllPolicyListenersLocked(); - notifyAllPermissionedStatusCallbacksLocked(mSubGroup, VCN_STATUS_CODE_SAFE_MODE); + notifyAllPermissionedStatusCallbacksLocked(mSubGroup, status); } } diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java index 54689358802f..ae806aa500a6 100644 --- a/services/core/java/com/android/server/vcn/Vcn.java +++ b/services/core/java/com/android/server/vcn/Vcn.java @@ -17,6 +17,7 @@ package com.android.server.vcn; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE; import static android.net.vcn.VcnManager.VCN_STATUS_CODE_INACTIVE; import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE; @@ -96,16 +97,20 @@ public class Vcn extends Handler { */ private static final int MSG_EVENT_GATEWAY_CONNECTION_QUIT = MSG_EVENT_BASE + 3; - /** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */ - private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE; - /** - * Causes this VCN to immediately enter safe mode. + * Triggers reevaluation of safe mode conditions. + * + * <p>Upon entering safe mode, the VCN will only provide gateway connections opportunistically, + * leaving the underlying networks marked as NOT_VCN_MANAGED. * - * <p>Upon entering safe mode, the VCN will unregister its RequestListener, tear down all of its - * VcnGatewayConnections, and notify VcnManagementService that it is in safe mode. + * <p>Any VcnGatewayConnection in safe mode will result in the entire Vcn instance being put + * into safe mode. Upon receiving this message, the Vcn MUST query all VcnGatewayConnections to + * determine if any are in safe mode. */ - private static final int MSG_CMD_ENTER_SAFE_MODE = MSG_CMD_BASE + 1; + private static final int MSG_EVENT_SAFE_MODE_STATE_CHANGED = MSG_EVENT_BASE + 4; + + /** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */ + private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE; @NonNull private final VcnContext mVcnContext; @NonNull private final ParcelUuid mSubscriptionGroup; @@ -233,6 +238,11 @@ public class Vcn extends Handler { @Override public void handleMessage(@NonNull Message msg) { + if (mCurrentStatus != VCN_STATUS_CODE_ACTIVE + && mCurrentStatus != VCN_STATUS_CODE_SAFE_MODE) { + return; + } + switch (msg.what) { case MSG_EVENT_CONFIG_UPDATED: handleConfigUpdated((VcnConfig) msg.obj); @@ -246,12 +256,12 @@ public class Vcn extends Handler { case MSG_EVENT_GATEWAY_CONNECTION_QUIT: handleGatewayConnectionQuit((VcnGatewayConnectionConfig) msg.obj); break; + case MSG_EVENT_SAFE_MODE_STATE_CHANGED: + handleSafeModeStatusChanged(); + break; case MSG_CMD_TEARDOWN: handleTeardown(); break; - case MSG_CMD_ENTER_SAFE_MODE: - handleEnterSafeMode(); - break; default: Slog.wtf(getLogTag(), "Unknown msg.what: " + msg.what); } @@ -263,40 +273,28 @@ public class Vcn extends Handler { mConfig = config; - // TODO(b/183174340): Remove this once opportunistic safe mode is supported. - if (mCurrentStatus == VCN_STATUS_CODE_ACTIVE) { - // VCN is already active - teardown any GatewayConnections whose configs have been - // removed and get all current requests - for (final Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry : - mVcnGatewayConnections.entrySet()) { - final VcnGatewayConnectionConfig gatewayConnectionConfig = entry.getKey(); - final VcnGatewayConnection gatewayConnection = entry.getValue(); - - // GatewayConnectionConfigs must match exactly (otherwise authentication or - // connection details may have changed). - if (!mConfig.getGatewayConnectionConfigs().contains(gatewayConnectionConfig)) { - if (gatewayConnection == null) { - Slog.wtf( - getLogTag(), - "Found gatewayConnectionConfig without GatewayConnection"); - } else { - gatewayConnection.teardownAsynchronously(); - } + // Teardown any GatewayConnections whose configs have been removed and get all current + // requests + for (final Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry : + mVcnGatewayConnections.entrySet()) { + final VcnGatewayConnectionConfig gatewayConnectionConfig = entry.getKey(); + final VcnGatewayConnection gatewayConnection = entry.getValue(); + + // GatewayConnectionConfigs must match exactly (otherwise authentication or + // connection details may have changed). + if (!mConfig.getGatewayConnectionConfigs().contains(gatewayConnectionConfig)) { + if (gatewayConnection == null) { + Slog.wtf( + getLogTag(), "Found gatewayConnectionConfig without GatewayConnection"); + } else { + gatewayConnection.teardownAsynchronously(); } } - - // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be - // satisfied start a new GatewayConnection) - mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener); - } else if (mCurrentStatus == VCN_STATUS_CODE_SAFE_MODE) { - // If this VCN was not previously active, it is exiting Safe Mode. Re-register the - // request listener to get NetworkRequests again (and all cached requests). - mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener); - } else { - // Ignored; VCN was not active; config updates ignored. - return; } - mCurrentStatus = VCN_STATUS_CODE_ACTIVE; + + // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be + // satisfied start a new GatewayConnection) + mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener); } private void handleTeardown() { @@ -309,21 +307,27 @@ public class Vcn extends Handler { mCurrentStatus = VCN_STATUS_CODE_INACTIVE; } - private void handleEnterSafeMode() { - // TODO(b/183174340): Remove this once opportunistic-safe-mode is supported - handleTeardown(); + private void handleSafeModeStatusChanged() { + boolean hasSafeModeGatewayConnection = false; - mCurrentStatus = VCN_STATUS_CODE_SAFE_MODE; - mVcnCallback.onEnteredSafeMode(); + // If any VcnGatewayConnection is in safe mode, mark the entire VCN as being in safe mode + for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) { + if (gatewayConnection.isInSafeMode()) { + hasSafeModeGatewayConnection = true; + break; + } + } + + final int oldStatus = mCurrentStatus; + mCurrentStatus = + hasSafeModeGatewayConnection ? VCN_STATUS_CODE_SAFE_MODE : VCN_STATUS_CODE_ACTIVE; + if (oldStatus != mCurrentStatus) { + mVcnCallback.onSafeModeStatusChanged(hasSafeModeGatewayConnection); + } } private void handleNetworkRequested( @NonNull NetworkRequest request, int score, int providerId) { - if (mCurrentStatus != VCN_STATUS_CODE_ACTIVE) { - Slog.v(getLogTag(), "Received NetworkRequest while inactive. Ignore for now"); - return; - } - if (score > getNetworkScore()) { if (VDBG) { Slog.v( @@ -376,25 +380,23 @@ public class Vcn extends Handler { mVcnGatewayConnections.remove(config); // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be satisfied - // start a new GatewayConnection), but only if the Vcn is still alive - if (mCurrentStatus == VCN_STATUS_CODE_ACTIVE) { - mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener); - } + // start a new GatewayConnection). VCN is always alive here, courtesy of the liveness check + // in handleMessage() + mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener); } private void handleSubscriptionsChanged(@NonNull TelephonySubscriptionSnapshot snapshot) { mLastSnapshot = snapshot; - if (mCurrentStatus == VCN_STATUS_CODE_ACTIVE) { - for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) { - gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot); - } + for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) { + gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot); } } private boolean isRequestSatisfiedByGatewayConnectionConfig( @NonNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config) { final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(); + builder.addTransportType(TRANSPORT_CELLULAR); builder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); for (int cap : config.getAllExposedCapabilities()) { builder.addCapability(cap); @@ -418,8 +420,8 @@ public class Vcn extends Handler { /** Callback used for passing status signals from a VcnGatewayConnection to its managing Vcn. */ @VisibleForTesting(visibility = Visibility.PACKAGE) public interface VcnGatewayStatusCallback { - /** Called by a VcnGatewayConnection to indicate that it has entered safe mode. */ - void onEnteredSafeMode(); + /** Called by a VcnGatewayConnection to indicate that it's safe mode status has changed. */ + void onSafeModeStatusChanged(); /** Callback by a VcnGatewayConnection to indicate that an error occurred. */ void onGatewayConnectionError( @@ -445,8 +447,8 @@ public class Vcn extends Handler { } @Override - public void onEnteredSafeMode() { - sendMessage(obtainMessage(MSG_CMD_ENTER_SAFE_MODE)); + public void onSafeModeStatusChanged() { + sendMessage(obtainMessage(MSG_EVENT_SAFE_MODE_STATE_CHANGED)); } @Override diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index 2ba8edd3b1d0..20c08eb2ce92 100644 --- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -32,6 +32,7 @@ import static com.android.server.VcnManagementService.VDBG; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.net.ConnectivityManager; import android.net.InetAddresses; import android.net.IpPrefix; import android.net.IpSecManager; @@ -44,6 +45,7 @@ import android.net.Network; import android.net.NetworkAgent; import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; +import android.net.NetworkProvider; import android.net.RouteInfo; import android.net.TelephonyNetworkSpecifier; import android.net.Uri; @@ -92,6 +94,7 @@ import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; /** * A single VCN Gateway Connection, providing a single public-facing VCN network. @@ -504,6 +507,15 @@ public class VcnGatewayConnection extends StateMachine { private boolean mIsQuitting = false; /** + * Whether the VcnGatewayConnection is in safe mode. + * + * <p>Upon hitting the safe mode timeout, this will be set to {@code true}. In safe mode, this + * VcnGatewayConnection will continue attempting to connect, and if a successful connection is + * made, safe mode will be exited. + */ + private boolean mIsInSafeMode = false; + + /** * The token used by the primary/current/active session. * * <p>This token MUST be updated when a new stateful/async session becomes the @@ -562,8 +574,7 @@ public class VcnGatewayConnection extends StateMachine { * <p>Set in Connected state, always @NonNull in Connected, Migrating states, @Nullable * otherwise. */ - @VisibleForTesting(visibility = Visibility.PRIVATE) - NetworkAgent mNetworkAgent; + private NetworkAgent mNetworkAgent; @Nullable private WakeupMessage mTeardownTimeoutAlarm; @Nullable private WakeupMessage mDisconnectRequestAlarm; @@ -628,6 +639,14 @@ public class VcnGatewayConnection extends StateMachine { start(); } + /** Queries whether this VcnGatewayConnection is in safe mode. */ + public boolean isInSafeMode() { + // Accessing internal state; must only be done on looper thread. + mVcnContext.ensureRunningOnLooperThread(); + + return mIsInSafeMode; + } + /** * Asynchronously tears down this GatewayConnection, and any resources used. * @@ -1162,6 +1181,15 @@ public class VcnGatewayConnection extends StateMachine { } } + protected void handleSafeModeTimeoutExceeded() { + mSafeModeTimeoutAlarm = null; + + // Connectivity for this GatewayConnection is broken; tear down the Network. + teardownNetwork(); + mIsInSafeMode = true; + mGatewayStatusCallback.onSafeModeStatusChanged(); + } + protected void logUnexpectedEvent(int what) { Slog.d(TAG, String.format( "Unexpected event code %d in state %s", what, this.getClass().getSimpleName())); @@ -1315,8 +1343,7 @@ public class VcnGatewayConnection extends StateMachine { } break; case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED: - mGatewayStatusCallback.onEnteredSafeMode(); - mSafeModeTimeoutAlarm = null; + handleSafeModeTimeoutExceeded(); break; default: logUnhandledMessage(msg); @@ -1401,8 +1428,7 @@ public class VcnGatewayConnection extends StateMachine { handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj); break; case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED: - mGatewayStatusCallback.onEnteredSafeMode(); - mSafeModeTimeoutAlarm = null; + handleSafeModeTimeoutExceeded(); break; default: logUnhandledMessage(msg); @@ -1432,30 +1458,35 @@ public class VcnGatewayConnection extends StateMachine { buildNetworkCapabilities(mConnectionConfig, mUnderlying); final LinkProperties lp = buildConnectedLinkProperties(mConnectionConfig, tunnelIface, childConfig); + final NetworkAgentConfig nac = + new NetworkAgentConfig.Builder() + .setLegacyType(ConnectivityManager.TYPE_MOBILE) + .build(); final NetworkAgent agent = - new NetworkAgent( - mVcnContext.getContext(), - mVcnContext.getLooper(), + mDeps.newNetworkAgent( + mVcnContext, TAG, caps, lp, Vcn.getNetworkScore(), - new NetworkAgentConfig.Builder().build(), - mVcnContext.getVcnNetworkProvider()) { - @Override - public void onNetworkUnwanted() { - Slog.d(TAG, "NetworkAgent was unwanted"); - teardownAsynchronously(); - } - - @Override - public void onValidationStatus(int status, @Nullable Uri redirectUri) { - if (status == NetworkAgent.VALIDATION_STATUS_VALID) { - clearFailedAttemptCounterAndSafeModeAlarm(); - } - } - }; + nac, + mVcnContext.getVcnNetworkProvider(), + () -> { + Slog.d(TAG, "NetworkAgent was unwanted"); + // If network agent has already been torn down, skip sending the + // disconnect. Unwanted() is always called, even when networkAgents + // are unregistered in teardownNetwork(), so prevent duplicate + // notifications. + if (mNetworkAgent != null) { + teardownAsynchronously(); + } + } /* networkUnwantedCallback */, + (status) -> { + if (status == NetworkAgent.VALIDATION_STATUS_VALID) { + clearFailedAttemptCounterAndSafeModeAlarm(); + } + } /* validationStatusCallback */); agent.register(); agent.markConnected(); @@ -1469,6 +1500,11 @@ public class VcnGatewayConnection extends StateMachine { // Validated connection, clear failed attempt counter mFailedAttempts = 0; cancelSafeModeAlarm(); + + if (mIsInSafeMode) { + mIsInSafeMode = false; + mGatewayStatusCallback.onSafeModeStatusChanged(); + } } protected void applyTransform( @@ -1491,13 +1527,6 @@ public class VcnGatewayConnection extends StateMachine { protected void setupInterface( int token, @NonNull IpSecTunnelInterface tunnelIface, - @NonNull VcnChildSessionConfiguration childConfig) { - setupInterface(token, tunnelIface, childConfig, null); - } - - protected void setupInterface( - int token, - @NonNull IpSecTunnelInterface tunnelIface, @NonNull VcnChildSessionConfiguration childConfig, @Nullable VcnChildSessionConfiguration oldChildConfig) { try { @@ -1579,16 +1608,17 @@ public class VcnGatewayConnection extends StateMachine { transformCreatedInfo.direction); break; case EVENT_SETUP_COMPLETED: + final VcnChildSessionConfiguration oldChildConfig = mChildConfig; mChildConfig = ((EventSetupCompletedInfo) msg.obj).childSessionConfig; - setupInterfaceAndNetworkAgent(mCurrentToken, mTunnelIface, mChildConfig); + setupInterfaceAndNetworkAgent( + mCurrentToken, mTunnelIface, mChildConfig, oldChildConfig); break; case EVENT_DISCONNECT_REQUESTED: handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj); break; case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED: - mGatewayStatusCallback.onEnteredSafeMode(); - mSafeModeTimeoutAlarm = null; + handleSafeModeTimeoutExceeded(); break; default: logUnhandledMessage(msg); @@ -1626,8 +1656,9 @@ public class VcnGatewayConnection extends StateMachine { protected void setupInterfaceAndNetworkAgent( int token, @NonNull IpSecTunnelInterface tunnelIface, - @NonNull VcnChildSessionConfiguration childConfig) { - setupInterface(token, tunnelIface, childConfig); + @NonNull VcnChildSessionConfiguration childConfig, + @NonNull VcnChildSessionConfiguration oldChildConfig) { + setupInterface(token, tunnelIface, childConfig, oldChildConfig); if (mNetworkAgent == null) { mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig); @@ -1692,8 +1723,7 @@ public class VcnGatewayConnection extends StateMachine { handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj); break; case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED: - mGatewayStatusCallback.onEnteredSafeMode(); - mSafeModeTimeoutAlarm = null; + handleSafeModeTimeoutExceeded(); break; default: logUnhandledMessage(msg); @@ -1935,6 +1965,16 @@ public class VcnGatewayConnection extends StateMachine { } @VisibleForTesting(visibility = Visibility.PRIVATE) + NetworkAgent getNetworkAgent() { + return mNetworkAgent; + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + void setNetworkAgent(@Nullable NetworkAgent networkAgent) { + mNetworkAgent = networkAgent; + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) void sendDisconnectRequestedAndAcquireWakelock(String reason, boolean shouldQuit) { sendMessageAndAcquireWakeLock( EVENT_DISCONNECT_REQUESTED, @@ -2018,6 +2058,38 @@ public class VcnGatewayConnection extends StateMachine { return new WakeupMessage(vcnContext.getContext(), handler, tag, runnable); } + /** Builds a new NetworkAgent. */ + public NetworkAgent newNetworkAgent( + @NonNull VcnContext vcnContext, + @NonNull String tag, + @NonNull NetworkCapabilities caps, + @NonNull LinkProperties lp, + @NonNull int score, + @NonNull NetworkAgentConfig nac, + @NonNull NetworkProvider provider, + @NonNull Runnable networkUnwantedCallback, + @NonNull Consumer<Integer> validationStatusCallback) { + return new NetworkAgent( + vcnContext.getContext(), + vcnContext.getLooper(), + tag, + caps, + lp, + score, + nac, + provider) { + @Override + public void onNetworkUnwanted() { + networkUnwantedCallback.run(); + } + + @Override + public void onValidationStatus(int status, @Nullable Uri redirectUri) { + validationStatusCallback.accept(status); + } + }; + } + /** Gets the elapsed real time since boot, in millis. */ public long getElapsedRealTime() { return SystemClock.elapsedRealtime(); diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index 4ffbf3147ee6..43e6676e1d4c 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -537,17 +537,6 @@ public class VcnManagementServiceTest { } @Test - public void testSetVcnConfigInSafeModeNotifiesStatusCallback() throws Exception { - setupSubscriptionAndStartVcn(TEST_SUBSCRIPTION_ID, TEST_UUID_2, false /* isActive */); - mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_2, mMockStatusCallback, TEST_PACKAGE_NAME); - verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_SAFE_MODE); - - mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); - - verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_ACTIVE); - } - - @Test public void testClearVcnConfigRequiresNonSystemServer() throws Exception { doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid(); @@ -902,7 +891,9 @@ public class VcnManagementServiceTest { } private void triggerVcnSafeMode( - @NonNull ParcelUuid subGroup, @NonNull TelephonySubscriptionSnapshot snapshot) + @NonNull ParcelUuid subGroup, + @NonNull TelephonySubscriptionSnapshot snapshot, + boolean isInSafeMode) throws Exception { verify(mMockDeps) .newVcn( @@ -913,22 +904,32 @@ public class VcnManagementServiceTest { mVcnCallbackCaptor.capture()); VcnCallback vcnCallback = mVcnCallbackCaptor.getValue(); - vcnCallback.onEnteredSafeMode(); + vcnCallback.onSafeModeStatusChanged(isInSafeMode); } - @Test - public void testVcnEnteringSafeModeNotifiesPolicyListeners() throws Exception { + private void verifyVcnSafeModeChangesNotifiesPolicyListeners(boolean enterSafeMode) + throws Exception { TelephonySubscriptionSnapshot snapshot = triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1)); mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); - triggerVcnSafeMode(TEST_UUID_1, snapshot); + triggerVcnSafeMode(TEST_UUID_1, snapshot, enterSafeMode); verify(mMockPolicyListener).onPolicyChanged(); } - private void triggerVcnStatusCallbackOnEnteredSafeMode( + @Test + public void testVcnEnteringSafeModeNotifiesPolicyListeners() throws Exception { + verifyVcnSafeModeChangesNotifiesPolicyListeners(true /* enterSafeMode */); + } + + @Test + public void testVcnExitingSafeModeNotifiesPolicyListeners() throws Exception { + verifyVcnSafeModeChangesNotifiesPolicyListeners(false /* enterSafeMode */); + } + + private void triggerVcnStatusCallbackOnSafeModeStatusChanged( @NonNull ParcelUuid subGroup, @NonNull String pkgName, int uid, @@ -951,12 +952,13 @@ public class VcnManagementServiceTest { mVcnMgmtSvc.registerVcnStatusCallback(subGroup, mMockStatusCallback, pkgName); - triggerVcnSafeMode(subGroup, snapshot); + triggerVcnSafeMode(subGroup, snapshot, true /* enterSafeMode */); } @Test - public void testVcnStatusCallbackOnEnteredSafeModeWithCarrierPrivileges() throws Exception { - triggerVcnStatusCallbackOnEnteredSafeMode( + public void testVcnStatusCallbackOnSafeModeStatusChangedWithCarrierPrivileges() + throws Exception { + triggerVcnStatusCallbackOnSafeModeStatusChanged( TEST_UUID_1, TEST_PACKAGE_NAME, TEST_UID, @@ -967,8 +969,9 @@ public class VcnManagementServiceTest { } @Test - public void testVcnStatusCallbackOnEnteredSafeModeWithoutCarrierPrivileges() throws Exception { - triggerVcnStatusCallbackOnEnteredSafeMode( + public void testVcnStatusCallbackOnSafeModeStatusChangedWithoutCarrierPrivileges() + throws Exception { + triggerVcnStatusCallbackOnSafeModeStatusChanged( TEST_UUID_1, TEST_PACKAGE_NAME, TEST_UID, @@ -980,8 +983,9 @@ public class VcnManagementServiceTest { } @Test - public void testVcnStatusCallbackOnEnteredSafeModeWithoutLocationPermission() throws Exception { - triggerVcnStatusCallbackOnEnteredSafeMode( + public void testVcnStatusCallbackOnSafeModeStatusChangedWithoutLocationPermission() + throws Exception { + triggerVcnStatusCallbackOnSafeModeStatusChanged( TEST_UUID_1, TEST_PACKAGE_NAME, TEST_UID, diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java index 2fadd44440f3..34c00182f855 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java @@ -32,6 +32,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -40,6 +41,8 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; +import android.net.ConnectivityManager; +import android.net.LinkAddress; import android.net.LinkProperties; import android.net.NetworkAgent; import android.net.NetworkCapabilities; @@ -58,19 +61,29 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import java.io.IOException; +import java.net.InetAddress; import java.net.UnknownHostException; +import java.util.Arrays; import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; /** Tests for VcnGatewayConnection.ConnectedState */ @RunWith(AndroidJUnit4.class) @SmallTest public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnectionTestBase { private VcnIkeSession mIkeSession; + private NetworkAgent mNetworkAgent; @Before public void setUp() throws Exception { super.setUp(); + mNetworkAgent = mock(NetworkAgent.class); + doReturn(mNetworkAgent) + .when(mDeps) + .newNetworkAgent(any(), any(), any(), any(), anyInt(), any(), any(), any(), any()); + mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1); mIkeSession = mGatewayConnection.buildIkeSession(TEST_UNDERLYING_NETWORK_RECORD_1.network); @@ -159,21 +172,44 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState()); } - @Test - public void testChildOpenedRegistersNetwork() throws Exception { - // Verify scheduled but not canceled when entering ConnectedState - verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */); + private void triggerChildOpened() { + triggerChildOpened(Collections.singletonList(TEST_INTERNAL_ADDR), TEST_DNS_ADDR); + } + private void triggerChildOpened(List<LinkAddress> internalAddresses, InetAddress dnsAddress) { final VcnChildSessionConfiguration mMockChildSessionConfig = mock(VcnChildSessionConfiguration.class); - doReturn(Collections.singletonList(TEST_INTERNAL_ADDR)) - .when(mMockChildSessionConfig) - .getInternalAddresses(); - doReturn(Collections.singletonList(TEST_DNS_ADDR)) + doReturn(internalAddresses).when(mMockChildSessionConfig).getInternalAddresses(); + doReturn(Collections.singletonList(dnsAddress)) .when(mMockChildSessionConfig) .getInternalDnsServers(); getChildSessionCallback().onOpened(mMockChildSessionConfig); + } + + private void triggerValidation(int status) { + final ArgumentCaptor<Consumer<Integer>> validationCallbackCaptor = + ArgumentCaptor.forClass(Consumer.class); + verify(mDeps) + .newNetworkAgent( + any(), + any(), + any(), + any(), + anyInt(), + any(), + any(), + any(), + validationCallbackCaptor.capture()); + + validationCallbackCaptor.getValue().accept(status); + } + + @Test + public void testChildOpenedRegistersNetwork() throws Exception { + // Verify scheduled but not canceled when entering ConnectedState + verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */); + triggerChildOpened(); mTestLooper.dispatchAll(); assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState()); @@ -182,15 +218,20 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection ArgumentCaptor.forClass(LinkProperties.class); final ArgumentCaptor<NetworkCapabilities> ncCaptor = ArgumentCaptor.forClass(NetworkCapabilities.class); - verify(mConnMgr) - .registerNetworkAgent( - any(), - any(), - lpCaptor.capture(), + verify(mDeps) + .newNetworkAgent( + eq(mVcnContext), + any(String.class), ncCaptor.capture(), + lpCaptor.capture(), + anyInt(), + argThat(nac -> nac.getLegacyType() == ConnectivityManager.TYPE_MOBILE), any(), any(), - anyInt()); + any()); + verify(mNetworkAgent).register(); + verify(mNetworkAgent).markConnected(); + verify(mIpSecSvc) .addAddressToTunnelInterface( eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(TEST_INTERNAL_ADDR), any()); @@ -208,9 +249,78 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection // Now that Vcn Network is up, notify it as validated and verify the SafeMode alarm is // canceled - mGatewayConnection.mNetworkAgent.onValidationStatus( - NetworkAgent.VALIDATION_STATUS_VALID, null /* redirectUri */); + triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID); verify(mSafeModeTimeoutAlarm).cancel(); + assertFalse(mGatewayConnection.isInSafeMode()); + } + + @Test + public void testInternalAndDnsAddressesChanged() throws Exception { + final List<LinkAddress> startingInternalAddrs = + Arrays.asList(new LinkAddress[] {TEST_INTERNAL_ADDR, TEST_INTERNAL_ADDR_2}); + triggerChildOpened(startingInternalAddrs, TEST_DNS_ADDR); + mTestLooper.dispatchAll(); + + for (LinkAddress addr : startingInternalAddrs) { + verify(mIpSecSvc) + .addAddressToTunnelInterface( + eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(addr), any()); + } + + verify(mDeps) + .newNetworkAgent( + any(), + any(), + any(), + argThat( + lp -> + startingInternalAddrs.equals(lp.getLinkAddresses()) + && Collections.singletonList(TEST_DNS_ADDR) + .equals(lp.getDnsServers())), + anyInt(), + any(), + any(), + any(), + any()); + + // Trigger another connection event, and verify that the addresses change + final List<LinkAddress> newInternalAddrs = + Arrays.asList(new LinkAddress[] {TEST_INTERNAL_ADDR_2, TEST_INTERNAL_ADDR_3}); + triggerChildOpened(newInternalAddrs, TEST_DNS_ADDR_2); + mTestLooper.dispatchAll(); + + // Verify addresses on tunnel network added/removed + for (LinkAddress addr : newInternalAddrs) { + verify(mIpSecSvc) + .addAddressToTunnelInterface( + eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(addr), any()); + } + verify(mIpSecSvc) + .removeAddressFromTunnelInterface( + eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(TEST_INTERNAL_ADDR), any()); + + // TODO(b/184579891): Also verify link properties updated and sent when sendLinkProperties + // is mockable + + // Verify that IpSecTunnelInterface only created once + verify(mIpSecSvc).createTunnelInterface(any(), any(), any(), any(), any()); + verifyNoMoreInteractions(mIpSecSvc); + } + + @Test + public void testSuccessfulConnectionExitsSafeMode() throws Exception { + verifySafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent( + mGatewayConnection.mConnectedState); + + assertTrue(mGatewayConnection.isInSafeMode()); + assertFalse(mGatewayConnection.isQuitting()); + + triggerChildOpened(); + mTestLooper.dispatchAll(); + + triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID); + + assertFalse(mGatewayConnection.isInSafeMode()); } @Test diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java index 7afa4494ee8b..bfe8c73d6389 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java @@ -118,8 +118,9 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio } @Test - public void testSafeModeTimeoutNotifiesCallback() { - verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mConnectingState); + public void testSafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent() { + verifySafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent( + mGatewayConnection.mConnectingState); } @Test diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java index 99feffdebc8e..9da8b451c9fc 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java @@ -86,8 +86,9 @@ public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnec } @Test - public void testSafeModeTimeoutNotifiesCallback() { - verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mDisconnectingState); + public void testSafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent() { + verifySafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent( + mGatewayConnection.mDisconnectingState); } @Test diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java index 85a0277f8b48..6dbf7d552bb6 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java @@ -96,8 +96,9 @@ public class VcnGatewayConnectionRetryTimeoutStateTest extends VcnGatewayConnect } @Test - public void testSafeModeTimeoutNotifiesCallback() { - verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mRetryTimeoutState); + public void testSafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent() { + verifySafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent( + mGatewayConnection.mRetryTimeoutState); } @Test diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java index a660735470a4..c5ed8f6ddcc7 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java @@ -21,6 +21,8 @@ import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession; import static com.android.server.vcn.VcnTestUtils.setupIpSecManager; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.atLeastOnce; @@ -42,6 +44,7 @@ import android.net.IpSecTunnelInterfaceResponse; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.Network; +import android.net.NetworkAgent; import android.net.NetworkCapabilities; import android.net.ipsec.ike.ChildSessionCallback; import android.net.ipsec.ike.IkeSessionCallback; @@ -71,8 +74,14 @@ public class VcnGatewayConnectionTestBase { protected static final ParcelUuid TEST_SUB_GRP = new ParcelUuid(UUID.randomUUID()); protected static final InetAddress TEST_DNS_ADDR = InetAddresses.parseNumericAddress("2001:DB8:0:1::"); + protected static final InetAddress TEST_DNS_ADDR_2 = + InetAddresses.parseNumericAddress("2001:DB8:0:2::"); protected static final LinkAddress TEST_INTERNAL_ADDR = - new LinkAddress(InetAddresses.parseNumericAddress("2001:DB8:0:2::"), 64); + new LinkAddress(InetAddresses.parseNumericAddress("2001:DB8:1:1::"), 64); + protected static final LinkAddress TEST_INTERNAL_ADDR_2 = + new LinkAddress(InetAddresses.parseNumericAddress("2001:DB8:1:2::"), 64); + protected static final LinkAddress TEST_INTERNAL_ADDR_3 = + new LinkAddress(InetAddresses.parseNumericAddress("2001:DB8:1:3::"), 64); protected static final int TEST_IPSEC_SPI_VALUE = 0x1234; protected static final int TEST_IPSEC_SPI_RESOURCE_ID = 1; @@ -267,7 +276,12 @@ public class VcnGatewayConnectionTestBase { expectCanceled); } - protected void verifySafeModeTimeoutNotifiesCallback(@NonNull State expectedState) { + protected void verifySafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent( + @NonNull State expectedState) { + // Set a NetworkAgent, and expect it to be unregistered and cleared + final NetworkAgent mockNetworkAgent = mock(NetworkAgent.class); + mGatewayConnection.setNetworkAgent(mockNetworkAgent); + // SafeMode timer starts when VcnGatewayConnection exits DisconnectedState (the initial // state) final Runnable delayedEvent = @@ -275,7 +289,11 @@ public class VcnGatewayConnectionTestBase { delayedEvent.run(); mTestLooper.dispatchAll(); - verify(mGatewayStatusCallback).onEnteredSafeMode(); + verify(mGatewayStatusCallback).onSafeModeStatusChanged(); assertEquals(expectedState, mGatewayConnection.getCurrentState()); + assertTrue(mGatewayConnection.isInSafeMode()); + + verify(mockNetworkAgent).unregister(); + assertNull(mGatewayConnection.getNetworkAgent()); } } diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java index 540be38ed798..90eb75e865e7 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java @@ -19,14 +19,13 @@ package com.android.server.vcn; import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE; -import static android.net.vcn.VcnManager.VCN_STATUS_CODE_INACTIVE; import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; @@ -54,7 +53,6 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.UUID; @@ -136,6 +134,7 @@ public class VcnTest { private void startVcnGatewayWithCapabilities( NetworkRequestListener requestListener, int... netCapabilities) { final NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder(); + requestBuilder.addTransportType(TRANSPORT_CELLULAR); for (final int netCapability : netCapabilities) { requestBuilder.addCapability(netCapability); } @@ -160,8 +159,7 @@ public class VcnTest { mTestLooper.dispatchAll(); for (final VcnGatewayConnection gateway : gatewayConnections) { - verify(gateway, status == VCN_STATUS_CODE_ACTIVE ? times(1) : never()) - .updateSubscriptionSnapshot(eq(updatedSnapshot)); + verify(gateway).updateSubscriptionSnapshot(eq(updatedSnapshot)); } } @@ -202,32 +200,53 @@ public class VcnTest { private void verifySafeMode( NetworkRequestListener requestListener, - Set<VcnGatewayConnection> expectedGatewaysTornDown) { - assertEquals(VCN_STATUS_CODE_SAFE_MODE, mVcn.getStatus()); - for (final VcnGatewayConnection gatewayConnection : expectedGatewaysTornDown) { - verify(gatewayConnection).teardownAsynchronously(); + Set<VcnGatewayConnection> activeGateways, + boolean expectInSafeMode) { + for (VcnGatewayConnection gatewayConnection : activeGateways) { + verify(gatewayConnection, never()).teardownAsynchronously(); } - verify(mVcnNetworkProvider).unregisterListener(requestListener); - verify(mVcnCallback).onEnteredSafeMode(); + + assertEquals( + expectInSafeMode ? VCN_STATUS_CODE_SAFE_MODE : VCN_STATUS_CODE_ACTIVE, + mVcn.getStatus()); + verify(mVcnCallback).onSafeModeStatusChanged(expectInSafeMode); } @Test - public void testGatewayEnteringSafeModeNotifiesVcn() { + public void testGatewayEnteringAndExitingSafeModeNotifiesVcn() { final NetworkRequestListener requestListener = verifyAndGetRequestListener(); final Set<VcnGatewayConnection> gatewayConnections = startGatewaysAndGetGatewayConnections(requestListener); - // Doesn't matter which callback this gets - any Gateway entering Safemode should shut down - // all Gateways + // Doesn't matter which callback this gets, or which VCN is in safe mode - any Gateway + // entering Safemode should trigger safe mode final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue(); - statusCallback.onEnteredSafeMode(); + final VcnGatewayConnection gatewayConnection = gatewayConnections.iterator().next(); + + doReturn(true).when(gatewayConnection).isInSafeMode(); + statusCallback.onSafeModeStatusChanged(); + mTestLooper.dispatchAll(); + + verifySafeMode(requestListener, gatewayConnections, true /* expectInSafeMode */); + + // Verify that when all GatewayConnections exit safe mode, the VCN also exits safe mode + doReturn(false).when(gatewayConnection).isInSafeMode(); + statusCallback.onSafeModeStatusChanged(); + mTestLooper.dispatchAll(); + + verifySafeMode(requestListener, gatewayConnections, false /* expectInSafeMode */); + + // Re-trigger, verify safe mode callback does not get fired again for identical state + statusCallback.onSafeModeStatusChanged(); mTestLooper.dispatchAll(); - verifySafeMode(requestListener, gatewayConnections); + // Expect only once still; from above. + verify(mVcnCallback).onSafeModeStatusChanged(false); } - @Test - public void testGatewayQuit() { + private void verifyGatewayQuit(int status) { + mVcn.setStatus(status); + final NetworkRequestListener requestListener = verifyAndGetRequestListener(); final Set<VcnGatewayConnection> gatewayConnections = new ArraySet<>(startGatewaysAndGetGatewayConnections(requestListener)); @@ -240,7 +259,7 @@ public class VcnTest { assertEquals(1, mVcn.getVcnGatewayConnections().size()); verify(mVcnNetworkProvider).resendAllRequests(requestListener); - // Verify that the VcnGatewayConnection is restarted + // Verify that the VcnGatewayConnection is restarted if a request exists for it triggerVcnRequestListeners(requestListener); mTestLooper.dispatchAll(); assertEquals(2, mVcn.getVcnGatewayConnections().size()); @@ -254,21 +273,13 @@ public class VcnTest { } @Test - public void testGatewayQuitWhileInactive() { - final NetworkRequestListener requestListener = verifyAndGetRequestListener(); - final Set<VcnGatewayConnection> gatewayConnections = - new ArraySet<>(startGatewaysAndGetGatewayConnections(requestListener)); - - mVcn.teardownAsynchronously(); - mTestLooper.dispatchAll(); - - final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue(); - statusCallback.onQuit(); - mTestLooper.dispatchAll(); + public void testGatewayQuitReevaluatesRequests() { + verifyGatewayQuit(VCN_STATUS_CODE_ACTIVE); + } - // Verify that the VCN requests the networkRequests be resent - assertEquals(1, mVcn.getVcnGatewayConnections().size()); - verify(mVcnNetworkProvider, never()).resendAllRequests(requestListener); + @Test + public void testGatewayQuitReevaluatesRequestsInSafeMode() { + verifyGatewayQuit(VCN_STATUS_CODE_SAFE_MODE); } @Test @@ -298,49 +309,4 @@ public class VcnTest { verify(removedGatewayConnection).teardownAsynchronously(); verify(mVcnNetworkProvider).resendAllRequests(requestListener); } - - @Test - public void testUpdateConfigExitsSafeMode() { - final NetworkRequestListener requestListener = verifyAndGetRequestListener(); - final Set<VcnGatewayConnection> gatewayConnections = - new ArraySet<>(startGatewaysAndGetGatewayConnections(requestListener)); - - final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue(); - statusCallback.onEnteredSafeMode(); - mTestLooper.dispatchAll(); - verifySafeMode(requestListener, gatewayConnections); - - doAnswer(invocation -> { - final NetworkRequestListener listener = invocation.getArgument(0); - triggerVcnRequestListeners(listener); - return null; - }).when(mVcnNetworkProvider).registerListener(eq(requestListener)); - - mVcn.updateConfig(mConfig); - mTestLooper.dispatchAll(); - - // Registered on start, then re-registered with new configs - verify(mVcnNetworkProvider, times(2)).registerListener(eq(requestListener)); - assertEquals(VCN_STATUS_CODE_ACTIVE, mVcn.getStatus()); - for (final int[] caps : TEST_CAPS) { - // Expect each gateway connection created only on initial startup - verify(mDeps) - .newVcnGatewayConnection( - eq(mVcnContext), - eq(TEST_SUB_GROUP), - eq(mSubscriptionSnapshot), - argThat(config -> Arrays.equals(caps, config.getExposedCapabilities())), - any()); - } - } - - @Test - public void testIgnoreNetworkRequestWhileInactive() { - mVcn.setStatus(VCN_STATUS_CODE_INACTIVE); - - final NetworkRequestListener requestListener = verifyAndGetRequestListener(); - triggerVcnRequestListeners(requestListener); - - verify(mDeps, never()).newVcnGatewayConnection(any(), any(), any(), any(), any()); - } } |