Notify Vcn of VcnGatewayConnection quits

This change updates the Vcn when any GatewayConnection StateMachine
quits, removing it from the map of active GatewayConnections, and
triggering a re-evaluation of all existing NetworkRequests. This may (in
the event of a updated policy) result in an immediate restart.

Bug: 179944275
Test: atest FrameworksVcnTests
Change-Id: I72abf1e270a772760a17a03173d5a110e15bd01d
This commit is contained in:
Benedict Wong
2021-02-10 15:55:53 -08:00
parent 75a32b9401
commit 685c909718
5 changed files with 117 additions and 17 deletions

View File

@@ -16,6 +16,8 @@
package com.android.server.vcn;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static com.android.server.VcnManagementService.VDBG;
import android.annotation.NonNull;
@@ -84,6 +86,13 @@ public class Vcn extends Handler {
*/
private static final int MSG_EVENT_SUBSCRIPTIONS_CHANGED = MSG_EVENT_BASE + 2;
/**
* A GatewayConnection owned by this VCN quit.
*
* @param obj VcnGatewayConnectionConfig
*/
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;
@@ -208,6 +217,9 @@ public class Vcn extends Handler {
case MSG_EVENT_SUBSCRIPTIONS_CHANGED:
handleSubscriptionsChanged((TelephonySubscriptionSnapshot) msg.obj);
break;
case MSG_EVENT_GATEWAY_CONNECTION_QUIT:
handleGatewayConnectionQuit((VcnGatewayConnectionConfig) msg.obj);
break;
case MSG_CMD_TEARDOWN:
handleTeardown();
break;
@@ -263,7 +275,7 @@ public class Vcn extends Handler {
// If preexisting VcnGatewayConnection(s) satisfy request, return
for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) {
if (requestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
if (VDBG) {
Slog.v(
getLogTag(),
@@ -278,7 +290,7 @@ public class Vcn extends Handler {
// up
for (VcnGatewayConnectionConfig gatewayConnectionConfig :
mConfig.getGatewayConnectionConfigs()) {
if (requestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
Slog.v(
getLogTag(),
"Bringing up new VcnGatewayConnection for request " + request.requestId);
@@ -289,12 +301,21 @@ public class Vcn extends Handler {
mSubscriptionGroup,
mLastSnapshot,
gatewayConnectionConfig,
new VcnGatewayStatusCallbackImpl());
new VcnGatewayStatusCallbackImpl(gatewayConnectionConfig));
mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection);
}
}
}
private void handleGatewayConnectionQuit(VcnGatewayConnectionConfig config) {
Slog.v(getLogTag(), "VcnGatewayConnection quit: " + config);
mVcnGatewayConnections.remove(config);
// 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 handleSubscriptionsChanged(@NonNull TelephonySubscriptionSnapshot snapshot) {
mLastSnapshot = snapshot;
@@ -305,9 +326,10 @@ public class Vcn extends Handler {
}
}
private boolean requestSatisfiedByGatewayConnectionConfig(
private boolean isRequestSatisfiedByGatewayConnectionConfig(
@NonNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config) {
final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
builder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
for (int cap : config.getAllExposedCapabilities()) {
builder.addCapability(cap);
}
@@ -339,9 +361,23 @@ public class Vcn extends Handler {
@VcnErrorCode int errorCode,
@Nullable String exceptionClass,
@Nullable String exceptionMessage);
/** Called by a VcnGatewayConnection to indicate that it has fully torn down. */
void onQuit();
}
private class VcnGatewayStatusCallbackImpl implements VcnGatewayStatusCallback {
public final VcnGatewayConnectionConfig mGatewayConnectionConfig;
VcnGatewayStatusCallbackImpl(VcnGatewayConnectionConfig gatewayConnectionConfig) {
mGatewayConnectionConfig = gatewayConnectionConfig;
}
@Override
public void onQuit() {
sendMessage(obtainMessage(MSG_EVENT_GATEWAY_CONNECTION_QUIT, mGatewayConnectionConfig));
}
@Override
public void onEnteredSafeMode() {
sendMessage(obtainMessage(MSG_CMD_ENTER_SAFE_MODE));

View File

@@ -649,6 +649,8 @@ public class VcnGatewayConnection extends StateMachine {
cancelSafeModeAlarm();
mUnderlyingNetworkTracker.teardown();
mGatewayStatusCallback.onQuit();
}
/**

View File

@@ -67,9 +67,7 @@ public class VcnNetworkProvider extends NetworkProvider {
mListeners.add(listener);
// Send listener all cached requests
for (NetworkRequestEntry entry : mRequests.values()) {
notifyListenerForEvent(listener, entry);
}
resendAllRequests(listener);
}
/** Unregisters the specified listener from receiving future NetworkRequests. */
@@ -78,6 +76,14 @@ public class VcnNetworkProvider extends NetworkProvider {
mListeners.remove(listener);
}
/** Sends all cached NetworkRequest(s) to the specified listener. */
@VisibleForTesting(visibility = Visibility.PACKAGE)
public void resendAllRequests(@NonNull NetworkRequestListener listener) {
for (NetworkRequestEntry entry : mRequests.values()) {
notifyListenerForEvent(listener, entry);
}
}
private void notifyListenerForEvent(
@NonNull NetworkRequestListener listener, @NonNull NetworkRequestEntry entry) {
listener.onNetworkRequested(entry.mRequest, entry.mScore, entry.mProviderId);

View File

@@ -26,6 +26,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.net.IpSecManager;
@@ -104,6 +105,7 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
verify(mIpSecSvc).deleteTunnelInterface(eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), any());
verifySafeModeTimeoutAlarmAndGetCallback(true /* expectCanceled */);
assertFalse(mGatewayConnection.isRunning());
verify(mGatewayStatusCallback).onQuit();
}
@Test
@@ -113,6 +115,7 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
assertTrue(mGatewayConnection.isRunning());
verify(mGatewayStatusCallback, never()).onQuit();
// No safe mode timer changes expected.
}
}

View File

@@ -16,6 +16,10 @@
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 org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.Matchers.any;
@@ -33,6 +37,7 @@ import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.os.ParcelUuid;
import android.os.test.TestLooper;
import android.util.ArraySet;
import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
@@ -51,6 +56,11 @@ public class VcnTest {
private static final ParcelUuid TEST_SUB_GROUP = new ParcelUuid(new UUID(0, 0));
private static final int NETWORK_SCORE = 0;
private static final int PROVIDER_ID = 5;
private static final int[][] TEST_CAPS =
new int[][] {
new int[] {NET_CAPABILITY_INTERNET, NET_CAPABILITY_MMS},
new int[] {NET_CAPABILITY_DUN}
};
private Context mContext;
private VcnContext mVcnContext;
@@ -91,13 +101,12 @@ public class VcnTest {
mGatewayStatusCallbackCaptor = ArgumentCaptor.forClass(VcnGatewayStatusCallback.class);
final VcnConfig.Builder configBuilder = new VcnConfig.Builder(mContext);
for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
for (final int[] caps : TEST_CAPS) {
configBuilder.addGatewayConnectionConfig(
VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(capability));
VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(caps));
}
configBuilder.addGatewayConnectionConfig(VcnGatewayConnectionConfigTest.buildTestConfig());
mConfig = configBuilder.build();
mConfig = configBuilder.build();
mVcn =
new Vcn(
mVcnContext,
@@ -130,8 +139,7 @@ public class VcnTest {
@Test
public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() {
final NetworkRequestListener requestListener = verifyAndGetRequestListener();
startVcnGatewayWithCapabilities(
requestListener, VcnGatewayConnectionConfigTest.EXPOSED_CAPS);
startVcnGatewayWithCapabilities(requestListener, TEST_CAPS[0]);
final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
assertFalse(gatewayConnections.isEmpty());
@@ -153,10 +161,19 @@ public class VcnTest {
for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
startVcnGatewayWithCapabilities(requestListener, capability);
}
}
// Each Capability in EXPOSED_CAPS was split into a separate VcnGatewayConnection in #setUp.
// Expect one VcnGatewayConnection per capability.
final int numExpectedGateways = VcnGatewayConnectionConfigTest.EXPOSED_CAPS.length;
private void triggerVcnRequestListeners(NetworkRequestListener requestListener) {
for (final int[] caps : TEST_CAPS) {
startVcnGatewayWithCapabilities(requestListener, caps);
}
}
public Set<VcnGatewayConnection> startGatewaysAndGetGatewayConnections(
NetworkRequestListener requestListener) {
triggerVcnRequestListeners(requestListener);
final int numExpectedGateways = TEST_CAPS.length;
final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
assertEquals(numExpectedGateways, gatewayConnections.size());
@@ -168,7 +185,16 @@ public class VcnTest {
any(),
mGatewayStatusCallbackCaptor.capture());
// Doesn't matter which callback this gets - any Gateway entering safe mode should shut down
return gatewayConnections;
}
@Test
public void testGatewayEnteringSafemodeNotifiesVcn() {
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
final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
statusCallback.onEnteredSafeMode();
@@ -181,4 +207,31 @@ public class VcnTest {
verify(mVcnNetworkProvider).unregisterListener(requestListener);
verify(mVcnCallback).onEnteredSafeMode();
}
@Test
public void testGatewayQuit() {
final NetworkRequestListener requestListener = verifyAndGetRequestListener();
final Set<VcnGatewayConnection> gatewayConnections =
new ArraySet<>(startGatewaysAndGetGatewayConnections(requestListener));
final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
statusCallback.onQuit();
mTestLooper.dispatchAll();
// Verify that the VCN requests the networkRequests be resent
assertEquals(1, mVcn.getVcnGatewayConnections().size());
verify(mVcnNetworkProvider).resendAllRequests(requestListener);
// Verify that the VcnGatewayConnection is restarted
triggerVcnRequestListeners(requestListener);
mTestLooper.dispatchAll();
assertEquals(2, mVcn.getVcnGatewayConnections().size());
verify(mDeps, times(gatewayConnections.size() + 1))
.newVcnGatewayConnection(
eq(mVcnContext),
eq(TEST_SUB_GROUP),
eq(mSubscriptionSnapshot),
any(),
mGatewayStatusCallbackCaptor.capture());
}
}