Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1fe26df5 authored by Benedict Wong's avatar Benedict Wong
Browse files

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
parent e4f60f7d
Loading
Loading
Loading
Loading
+64 −16
Original line number Diff line number Diff line
@@ -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);

        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
        });
    }

    /**
@@ -311,15 +345,17 @@ public class VcnManagementService extends IVcnManagementService.Stub {

        enforceCallingUserAndCarrierPrivilege(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);
                }
            }
        }
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -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.
    }

+41 −1
Original line number Diff line number Diff line
@@ -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();
    }
}