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

Commit f38b347c authored by Cody Kesting's avatar Cody Kesting
Browse files

Call VcnStatusCallback#onVcnStatusChanged on register.

This CL updates VcnMangementService to notify VcnStatusCallbacks on
registration with the current status of the VCN for the specified
subscription group.

Bug: 180659281
Test: atest FrameworksVcnTests
Change-Id: Id2c74e855fa12d21d292ee94a72ad047f2d56aca
parent 5d623992
Loading
Loading
Loading
Loading
+44 −22
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.net.vcn.IVcnManagementService;
import android.net.vcn.IVcnStatusCallback;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnManager;
import android.net.vcn.VcnManager.VcnErrorCode;
import android.net.vcn.VcnUnderlyingNetworkPolicy;
import android.net.wifi.WifiInfo;
@@ -724,6 +725,26 @@ public class VcnManagementService extends IVcnManagementService.Stub {
        }
    }

    private boolean isCallbackPermissioned(
            @NonNull VcnStatusCallbackInfo cbInfo, @NonNull ParcelUuid subgroup) {
        if (!subgroup.equals(cbInfo.mSubGroup)) {
            return false;
        }

        if (!mLastSnapshot.packageHasPermissionsForSubscriptionGroup(subgroup, cbInfo.mPkgName)) {
            return false;
        }

        if (!mLocationPermissionChecker.checkLocationPermission(
                cbInfo.mPkgName,
                "VcnStatusCallback" /* featureId */,
                cbInfo.mUid,
                null /* message */)) {
            return false;
        }
        return true;
    }

    /** Registers the provided callback for receiving VCN status updates. */
    @Override
    public void registerVcnStatusCallback(
@@ -758,6 +779,27 @@ public class VcnManagementService extends IVcnManagementService.Stub {
                }

                mRegisteredStatusCallbacks.put(cbBinder, cbInfo);

                // now that callback is registered, send it the VCN's current status
                final VcnConfig vcnConfig = mConfigs.get(subGroup);
                final Vcn vcn = mVcns.get(subGroup);
                final int vcnStatus;
                if (vcnConfig == null || !isCallbackPermissioned(cbInfo, subGroup)) {
                    vcnStatus = VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED;
                } else if (vcn == null) {
                    vcnStatus = VcnManager.VCN_STATUS_CODE_INACTIVE;
                } else if (vcn.isActive()) {
                    vcnStatus = VcnManager.VCN_STATUS_CODE_ACTIVE;
                } else {
                    // TODO(b/181789060): create Vcn.getStatus() and Log.WTF() for unknown status
                    vcnStatus = VcnManager.VCN_STATUS_CODE_SAFE_MODE;
                }

                try {
                    cbInfo.mCallback.onVcnStatusChanged(vcnStatus);
                } catch (RemoteException e) {
                    Slog.d(TAG, "VcnStatusCallback threw on VCN status change", e);
                }
            }
        } finally {
            Binder.restoreCallingIdentity(identity);
@@ -806,26 +848,6 @@ public class VcnManagementService extends IVcnManagementService.Stub {
            mSubGroup = Objects.requireNonNull(subGroup, "Missing subGroup");
        }

        private boolean isCallbackPermissioned(@NonNull VcnStatusCallbackInfo cbInfo) {
            if (!mSubGroup.equals(cbInfo.mSubGroup)) {
                return false;
            }

            if (!mLastSnapshot.packageHasPermissionsForSubscriptionGroup(
                    mSubGroup, cbInfo.mPkgName)) {
                return false;
            }

            if (!mLocationPermissionChecker.checkLocationPermission(
                    cbInfo.mPkgName,
                    "VcnStatusCallback" /* featureId */,
                    cbInfo.mUid,
                    null /* message */)) {
                return false;
            }
            return true;
        }

        @Override
        public void onEnteredSafeMode() {
            synchronized (mLock) {
@@ -838,7 +860,7 @@ public class VcnManagementService extends IVcnManagementService.Stub {

                // Notify all registered StatusCallbacks for this subGroup
                for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) {
                    if (isCallbackPermissioned(cbInfo)) {
                    if (isCallbackPermissioned(cbInfo, mSubGroup)) {
                        Binder.withCleanCallingIdentity(
                                () ->
                                        cbInfo.mCallback.onVcnStatusChanged(
@@ -862,7 +884,7 @@ public class VcnManagementService extends IVcnManagementService.Stub {

                // Notify all registered StatusCallbacks for this subGroup
                for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) {
                    if (isCallbackPermissioned(cbInfo)) {
                    if (isCallbackPermissioned(cbInfo, mSubGroup)) {
                        Binder.withCleanCallingIdentity(
                                () ->
                                        cbInfo.mCallback.onGatewayConnectionError(
+114 −20
Original line number Diff line number Diff line
@@ -100,6 +100,8 @@ import java.util.UUID;
public class VcnManagementServiceTest {
    private static final String TEST_PACKAGE_NAME =
            VcnManagementServiceTest.class.getPackage().getName();
    private static final String TEST_CB_PACKAGE_NAME =
            VcnManagementServiceTest.class.getPackage().getName() + ".callback";
    private static final ParcelUuid TEST_UUID_1 = new ParcelUuid(new UUID(0, 0));
    private static final ParcelUuid TEST_UUID_2 = new ParcelUuid(new UUID(1, 1));
    private static final VcnConfig TEST_VCN_CONFIG;
@@ -288,6 +290,14 @@ public class VcnManagementServiceTest {

    private TelephonySubscriptionSnapshot triggerSubscriptionTrackerCbAndGetSnapshot(
            Set<ParcelUuid> activeSubscriptionGroups, Map<Integer, ParcelUuid> subIdToGroupMap) {
        return triggerSubscriptionTrackerCbAndGetSnapshot(
                activeSubscriptionGroups, subIdToGroupMap, true /* hasCarrierPrivileges */);
    }

    private TelephonySubscriptionSnapshot triggerSubscriptionTrackerCbAndGetSnapshot(
            Set<ParcelUuid> activeSubscriptionGroups,
            Map<Integer, ParcelUuid> subIdToGroupMap,
            boolean hasCarrierPrivileges) {
        final TelephonySubscriptionSnapshot snapshot = mock(TelephonySubscriptionSnapshot.class);
        doReturn(activeSubscriptionGroups).when(snapshot).getActiveSubscriptionGroups();

@@ -295,7 +305,7 @@ public class VcnManagementServiceTest {
                (activeSubscriptionGroups == null || activeSubscriptionGroups.isEmpty())
                        ? Collections.emptySet()
                        : Collections.singleton(TEST_PACKAGE_NAME);
        doReturn(true)
        doReturn(hasCarrierPrivileges)
                .when(snapshot)
                .packageHasPermissionsForSubscriptionGroup(
                        argThat(val -> activeSubscriptionGroups.contains(val)),
@@ -549,13 +559,6 @@ public class VcnManagementServiceTest {
        mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
    }

    private void setUpVcnSubscription(int subId, ParcelUuid subGroup) {
        mVcnMgmtSvc.setVcnConfig(subGroup, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);

        triggerSubscriptionTrackerCbAndGetSnapshot(
                Collections.singleton(subGroup), Collections.singletonMap(subId, subGroup));
    }

    private void verifyMergedNetworkCapabilities(
            NetworkCapabilities mergedCapabilities,
            @Transport int transportType,
@@ -573,9 +576,23 @@ public class VcnManagementServiceTest {
    }

    private void setupSubscriptionAndStartVcn(int subId, ParcelUuid subGrp, boolean isVcnActive) {
        setUpVcnSubscription(subId, subGrp);
        setupSubscriptionAndStartVcn(subId, subGrp, isVcnActive, true /* hasCarrierPrivileges */);
    }

    private void setupSubscriptionAndStartVcn(
            int subId, ParcelUuid subGrp, boolean isVcnActive, boolean hasCarrierPrivileges) {
        mVcnMgmtSvc.systemReady();
        triggerSubscriptionTrackerCbAndGetSnapshot(
                Collections.singleton(subGrp),
                Collections.singletonMap(subId, subGrp),
                hasCarrierPrivileges);

        final Vcn vcn = startAndGetVcnInstance(subGrp);
        doReturn(isVcnActive).when(vcn).isActive();

        doReturn(true)
                .when(mLocationPermissionChecker)
                .checkLocationPermission(eq(TEST_PACKAGE_NAME), any(), eq(TEST_UID), any());
    }

    private VcnUnderlyingNetworkPolicy startVcnAndGetPolicyForTransport(
@@ -721,7 +738,7 @@ public class VcnManagementServiceTest {
        verify(mMockPolicyListener).onPolicyChanged();
    }

    private void verifyVcnCallback(
    private void triggerVcnSafeMode(
            @NonNull ParcelUuid subGroup, @NonNull TelephonySubscriptionSnapshot snapshot)
            throws Exception {
        verify(mMockDeps)
@@ -732,20 +749,20 @@ public class VcnManagementServiceTest {
                        eq(snapshot),
                        mVcnCallbackCaptor.capture());

        mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);

        VcnCallback vcnCallback = mVcnCallbackCaptor.getValue();
        vcnCallback.onEnteredSafeMode();

        verify(mMockPolicyListener).onPolicyChanged();
    }

    @Test
    public void testVcnCallbackOnEnteredSafeMode() throws Exception {
    public void testVcnEnteringSafeModeNotifiesPolicyListeners() throws Exception {
        TelephonySubscriptionSnapshot snapshot =
                triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));

        verifyVcnCallback(TEST_UUID_1, snapshot);
        mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);

        triggerVcnSafeMode(TEST_UUID_1, snapshot);

        verify(mMockPolicyListener).onPolicyChanged();
    }

    private void triggerVcnStatusCallbackOnEnteredSafeMode(
@@ -758,6 +775,9 @@ public class VcnManagementServiceTest {
        TelephonySubscriptionSnapshot snapshot =
                triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(subGroup));

        setupSubscriptionAndStartVcn(
                TEST_SUBSCRIPTION_ID, subGroup, true /* isActive */, hasPermissionsforSubGroup);

        doReturn(hasPermissionsforSubGroup)
                .when(snapshot)
                .packageHasPermissionsForSubscriptionGroup(eq(subGroup), eq(pkgName));
@@ -768,10 +788,7 @@ public class VcnManagementServiceTest {

        mVcnMgmtSvc.registerVcnStatusCallback(subGroup, mMockStatusCallback, pkgName);

        // Trigger systemReady() to set up LocationPermissionChecker
        mVcnMgmtSvc.systemReady();

        verifyVcnCallback(subGroup, snapshot);
        triggerVcnSafeMode(subGroup, snapshot);
    }

    @Test
@@ -825,6 +842,83 @@ public class VcnManagementServiceTest {
        assertEquals(TEST_PACKAGE_NAME, cbInfo.mPkgName);
        assertEquals(TEST_UID, cbInfo.mUid);
        verify(mMockIBinder).linkToDeath(eq(cbInfo), anyInt());

        verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED);
    }

    @Test
    public void testRegisterVcnStatusCallback_MissingPermission() throws Exception {
        setupSubscriptionAndStartVcn(
                TEST_SUBSCRIPTION_ID,
                TEST_UUID_1,
                true /* isActive */,
                false /* hasCarrierPrivileges */);

        mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_1, mMockStatusCallback, TEST_PACKAGE_NAME);

        verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED);
    }

    @Test
    public void testRegisterVcnStatusCallback_VcnInactive() throws Exception {
        setupSubscriptionAndStartVcn(
                TEST_SUBSCRIPTION_ID,
                TEST_UUID_1,
                true /* isActive */,
                true /* hasCarrierPrivileges */);

        // VCN is currently active. Lose carrier privileges for TEST_PACKAGE and hit teardown
        // timeout so the VCN goes inactive.
        final TelephonySubscriptionSnapshot snapshot =
                triggerSubscriptionTrackerCbAndGetSnapshot(
                        Collections.singleton(TEST_UUID_1),
                        Collections.singletonMap(TEST_SUBSCRIPTION_ID, TEST_UUID_1),
                        false /* hasCarrierPrivileges */);
        mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
        mTestLooper.dispatchAll();

        // Giving TEST_PACKAGE privileges again will restart the VCN (which will indicate ACTIVE
        // when the status callback is registered). Instead, setup permissions for TEST_CB_PACKAGE
        // so that it's permissioned to receive INACTIVE (instead of NOT_CONFIGURED) without
        // reactivating the VCN.
        doReturn(true)
                .when(snapshot)
                .packageHasPermissionsForSubscriptionGroup(
                        eq(TEST_UUID_1), eq(TEST_CB_PACKAGE_NAME));
        doReturn(true)
                .when(mLocationPermissionChecker)
                .checkLocationPermission(eq(TEST_CB_PACKAGE_NAME), any(), eq(TEST_UID), any());

        mVcnMgmtSvc.registerVcnStatusCallback(
                TEST_UUID_1, mMockStatusCallback, TEST_CB_PACKAGE_NAME);

        verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_INACTIVE);
    }

    @Test
    public void testRegisterVcnStatusCallback_VcnActive() throws Exception {
        setupSubscriptionAndStartVcn(
                TEST_SUBSCRIPTION_ID,
                TEST_UUID_1,
                true /* isActive */,
                true /* hasCarrierPrivileges */);

        mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_1, mMockStatusCallback, TEST_PACKAGE_NAME);

        verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_ACTIVE);
    }

    @Test
    public void testRegisterVcnStatusCallback_VcnSafeMode() throws Exception {
        setupSubscriptionAndStartVcn(
                TEST_SUBSCRIPTION_ID,
                TEST_UUID_1,
                false /* isActive */,
                true /* hasCarrierPrivileges */);

        mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_1, mMockStatusCallback, TEST_PACKAGE_NAME);

        verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_SAFE_MODE);
    }

    @Test(expected = IllegalStateException.class)