Add basic VcnMgmtSvc --> Vcn signals (startup, teardown, NetworkReq)

This change adds the relevant calls to ensure that the VcnMgmtSvc
starts, updates and stops a Vcn instance when configs are set/removed

Additionally, this change ensures that upon new network requests, the
Vcn instance is notified.

Bug: 163432852
Test: atest FrameworksVcnTests
Change-Id: Ifec34fad8282a3d64b540d24f643f546463f4379
This commit is contained in:
Benedict Wong
2020-11-24 17:00:23 -08:00
parent e4f60f7dff
commit 1fe26df538
3 changed files with 106 additions and 18 deletions

View File

@@ -43,6 +43,8 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.server.vcn.Vcn;
import com.android.server.vcn.VcnContext;
import com.android.server.vcn.util.PersistableBundleUtils;
import java.io.IOException;
@@ -122,11 +124,16 @@ public class VcnManagementService extends IVcnManagementService.Stub {
@NonNull private final Looper mLooper;
@NonNull private final Handler mHandler;
@NonNull private final VcnNetworkProvider mNetworkProvider;
@NonNull private final VcnContext mVcnContext;
@GuardedBy("mLock")
@NonNull
private final Map<ParcelUuid, VcnConfig> mConfigs = new ArrayMap<>();
@GuardedBy("mLock")
@NonNull
private final Map<ParcelUuid, Vcn> mVcns = new ArrayMap<>();
@NonNull private final Object mLock = new Object();
@NonNull private final PersistableBundleUtils.LockingReadWriteHelper mConfigDiskRwHelper;
@@ -141,6 +148,7 @@ public class VcnManagementService extends IVcnManagementService.Stub {
mNetworkProvider = new VcnNetworkProvider(mContext, mLooper);
mConfigDiskRwHelper = mDeps.newPersistableBundleLockingReadWriteHelper(VCN_CONFIG_FILE);
mVcnContext = mDeps.newVcnContext(mContext, mLooper, mNetworkProvider);
// Run on handler to ensure I/O does not block system server startup
mHandler.post(() -> {
@@ -225,6 +233,22 @@ public class VcnManagementService extends IVcnManagementService.Stub {
newPersistableBundleLockingReadWriteHelper(@NonNull String path) {
return new PersistableBundleUtils.LockingReadWriteHelper(path);
}
/** Creates a new VcnContext */
public VcnContext newVcnContext(
@NonNull Context context,
@NonNull Looper looper,
@NonNull VcnNetworkProvider vcnNetworkProvider) {
return new VcnContext(context, looper, vcnNetworkProvider);
}
/** Creates a new Vcn instance using the provided configuration */
public Vcn newVcn(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config) {
return new Vcn(vcnContext, subscriptionGroup, config);
}
}
/** Notifies the VcnManagementService that external dependencies can be set up. */
@@ -277,6 +301,17 @@ public class VcnManagementService extends IVcnManagementService.Stub {
"Carrier privilege required for subscription group to set VCN Config");
}
@GuardedBy("mLock")
private void startOrUpdateVcnLocked(
@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) {
if (mVcns.containsKey(subscriptionGroup)) {
mVcns.get(subscriptionGroup).updateConfig(config);
} else {
final Vcn newInstance = mDeps.newVcn(mVcnContext, subscriptionGroup, config);
mVcns.put(subscriptionGroup, newInstance);
}
}
/**
* Sets a VCN config for a given subscription group.
*
@@ -289,15 +324,14 @@ public class VcnManagementService extends IVcnManagementService.Stub {
enforceCallingUserAndCarrierPrivilege(subscriptionGroup);
synchronized (mLock) {
mConfigs.put(subscriptionGroup, config);
Binder.withCleanCallingIdentity(() -> {
synchronized (mLock) {
mConfigs.put(subscriptionGroup, config);
startOrUpdateVcnLocked(subscriptionGroup, config);
// Must be done synchronously to ensure that writes do not happen out-of-order.
writeConfigsToDiskLocked();
}
// TODO: Clear Binder calling identity
// TODO: Trigger startup as necessary
writeConfigsToDiskLocked();
}
});
}
/**
@@ -311,15 +345,17 @@ public class VcnManagementService extends IVcnManagementService.Stub {
enforceCallingUserAndCarrierPrivilege(subscriptionGroup);
synchronized (mLock) {
mConfigs.remove(subscriptionGroup);
Binder.withCleanCallingIdentity(() -> {
synchronized (mLock) {
mConfigs.remove(subscriptionGroup);
// Must be done synchronously to ensure that writes do not happen out-of-order.
writeConfigsToDiskLocked();
}
if (mVcns.containsKey(subscriptionGroup)) {
mVcns.remove(subscriptionGroup).teardownAsynchronously();
}
// TODO: Clear Binder calling identity
// TODO: Trigger teardown as necessary
writeConfigsToDiskLocked();
}
});
}
@GuardedBy("mLock")
@@ -345,6 +381,14 @@ public class VcnManagementService extends IVcnManagementService.Stub {
}
}
/** Get current configuration list for testing purposes */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public Map<ParcelUuid, Vcn> getAllVcns() {
synchronized (mLock) {
return Collections.unmodifiableMap(mVcns);
}
}
/**
* Network provider for VCN networks.
*
@@ -357,7 +401,11 @@ public class VcnManagementService extends IVcnManagementService.Stub {
@Override
public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {
// TODO: Handle network requests - Ensure VCN started, and start appropriate tunnels.
synchronized (mLock) {
for (Vcn instance : mVcns.values()) {
instance.onNetworkRequested(request, score, providerId);
}
}
}
}
}

View File

@@ -69,7 +69,7 @@ public class Vcn extends Handler {
}
/** Asynchronously tears down this Vcn instance, along with all tunnels and Networks */
public void teardown() {
public void teardownAsynchronously() {
// TODO: Proxy to handler, and teardown there.
}

View File

@@ -21,6 +21,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
@@ -42,6 +43,9 @@ import android.telephony.TelephonyManager;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.VcnManagementService.VcnNetworkProvider;
import com.android.server.vcn.Vcn;
import com.android.server.vcn.VcnContext;
import com.android.server.vcn.util.PersistableBundleUtils;
import org.junit.Test;
@@ -92,10 +96,12 @@ public class VcnManagementServiceTest {
private final ConnectivityManager mConnMgr = mock(ConnectivityManager.class);
private final TelephonyManager mTelMgr = mock(TelephonyManager.class);
private final SubscriptionManager mSubMgr = mock(SubscriptionManager.class);
private final VcnManagementService mVcnMgmtSvc;
private final VcnContext mVcnContext = mock(VcnContext.class);
private final PersistableBundleUtils.LockingReadWriteHelper mConfigReadWriteHelper =
mock(PersistableBundleUtils.LockingReadWriteHelper.class);
private final VcnManagementService mVcnMgmtSvc;
public VcnManagementServiceTest() throws Exception {
setupSystemService(mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
setupSystemService(mTelMgr, Context.TELEPHONY_SERVICE, TelephonyManager.class);
@@ -104,10 +110,22 @@ public class VcnManagementServiceTest {
doReturn(mTestLooper.getLooper()).when(mMockDeps).getLooper();
doReturn(Process.FIRST_APPLICATION_UID).when(mMockDeps).getBinderCallingUid();
doReturn(mVcnContext)
.when(mMockDeps)
.newVcnContext(
eq(mMockContext),
eq(mTestLooper.getLooper()),
any(VcnNetworkProvider.class));
doReturn(mConfigReadWriteHelper)
.when(mMockDeps)
.newPersistableBundleLockingReadWriteHelper(any());
// Setup VCN instance generation
doAnswer((invocation) -> {
// Mock-within a doAnswer is safe, because it doesn't actually run nested.
return mock(Vcn.class);
}).when(mMockDeps).newVcn(any(), any(), any());
final PersistableBundle bundle =
PersistableBundleUtils.fromMap(
TEST_VCN_CONFIG_MAP,
@@ -255,4 +273,26 @@ public class VcnManagementServiceTest {
assertTrue(mVcnMgmtSvc.getConfigs().isEmpty());
verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
}
@Test
public void testSetVcnConfigClearVcnConfigStartsUpdatesAndTeardsDownVcns() throws Exception {
// Use a different UUID to simulate a new VCN config.
mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG);
final Map<ParcelUuid, Vcn> vcnInstances = mVcnMgmtSvc.getAllVcns();
final Vcn vcnInstance = vcnInstances.get(TEST_UUID_2);
assertEquals(1, vcnInstances.size());
assertEquals(TEST_VCN_CONFIG, mVcnMgmtSvc.getConfigs().get(TEST_UUID_2));
verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
// Verify Vcn is started
verify(mMockDeps).newVcn(eq(mVcnContext), eq(TEST_UUID_2), eq(TEST_VCN_CONFIG));
// Verify Vcn is updated if it was previously started
mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG);
verify(vcnInstance).updateConfig(TEST_VCN_CONFIG);
// Verify Vcn is stopped if it was already started
mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2);
verify(vcnInstance).teardownAsynchronously();
}
}