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

Commit 59881b17 authored by Cody Kesting's avatar Cody Kesting
Browse files

Notify status callbacks when a VCN enters Safemode.

This CL updates VcnManagementService to notify VcnStatusCallbacks when
the VCN for their specified subscription group enters Safemode. In order
to be notified, the registering app must also have permissions for the
specified permission.

Bug: 163433613
Test: atest FrameworksVcnTests
Change-Id: I3242ad0ee1dc406aef56253f884c2544a994869e
parent a3da6c88
Loading
Loading
Loading
Loading
+32 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubsc
import static java.util.Objects.requireNonNull;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.content.Context;
import android.net.ConnectivityManager;
@@ -55,6 +56,7 @@ 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.internal.util.LocationPermissionChecker;
import com.android.server.vcn.TelephonySubscriptionTracker;
import com.android.server.vcn.Vcn;
import com.android.server.vcn.VcnContext;
@@ -149,6 +151,9 @@ public class VcnManagementService extends IVcnManagementService.Stub {
    @NonNull private final TelephonySubscriptionTracker mTelephonySubscriptionTracker;
    @NonNull private final VcnContext mVcnContext;

    /** Can only be assigned when {@link #systemReady()} is called, since it uses AppOpsManager. */
    @Nullable private LocationPermissionChecker mLocationPermissionChecker;

    @GuardedBy("mLock")
    @NonNull
    private final Map<ParcelUuid, VcnConfig> mConfigs = new ArrayMap<>();
@@ -308,6 +313,11 @@ public class VcnManagementService extends IVcnManagementService.Stub {
            // TODO(b/178501049): use the subId indicated by WifiInfo#getSubscriptionId
            return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
        }

        /** Creates a new LocationPermissionChecker for the provided Context. */
        public LocationPermissionChecker newLocationPermissionChecker(@NonNull Context context) {
            return new LocationPermissionChecker(context);
        }
    }

    /** Notifies the VcnManagementService that external dependencies can be set up. */
@@ -315,6 +325,7 @@ public class VcnManagementService extends IVcnManagementService.Stub {
        mContext.getSystemService(ConnectivityManager.class)
                .registerNetworkProvider(mNetworkProvider);
        mTelephonySubscriptionTracker.register();
        mLocationPermissionChecker = mDeps.newLocationPermissionChecker(mVcnContext.getContext());
    }

    private void enforcePrimaryUser() {
@@ -796,6 +807,27 @@ public class VcnManagementService extends IVcnManagementService.Stub {
                }

                notifyAllPolicyListenersLocked();

                // Notify all registered StatusCallbacks for this subGroup
                for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) {
                    if (!mSubGroup.equals(cbInfo.mSubGroup)) {
                        continue;
                    }
                    if (!mLastSnapshot.packageHasPermissionsForSubscriptionGroup(
                            mSubGroup, cbInfo.mPkgName)) {
                        continue;
                    }

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

                    Binder.withCleanCallingIdentity(() -> cbInfo.mCallback.onEnteredSafeMode());
                }
            }
        }
    }
+83 −6
Original line number Diff line number Diff line
@@ -43,9 +43,11 @@ import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.annotation.NonNull;
import android.app.AppOpsManager;
import android.content.Context;
import android.net.ConnectivityManager;
@@ -72,6 +74,7 @@ import android.telephony.TelephonyManager;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.internal.util.LocationPermissionChecker;
import com.android.server.VcnManagementService.VcnSafeModeCallback;
import com.android.server.VcnManagementService.VcnStatusCallbackInfo;
import com.android.server.vcn.TelephonySubscriptionTracker;
@@ -150,6 +153,8 @@ public class VcnManagementServiceTest {
            mock(PersistableBundleUtils.LockingReadWriteHelper.class);
    private final TelephonySubscriptionTracker mSubscriptionTracker =
            mock(TelephonySubscriptionTracker.class);
    private final LocationPermissionChecker mLocationPermissionChecker =
            mock(LocationPermissionChecker.class);

    private final ArgumentCaptor<VcnSafeModeCallback> mSafeModeCallbackCaptor =
            ArgumentCaptor.forClass(VcnSafeModeCallback.class);
@@ -175,6 +180,7 @@ public class VcnManagementServiceTest {

        doReturn(TEST_PACKAGE_NAME).when(mMockContext).getOpPackageName();

        doReturn(mMockContext).when(mVcnContext).getContext();
        doReturn(mTestLooper.getLooper()).when(mMockDeps).getLooper();
        doReturn(TEST_UID).when(mMockDeps).getBinderCallingUid();
        doReturn(mVcnContext)
@@ -192,6 +198,9 @@ public class VcnManagementServiceTest {
        doReturn(mConfigReadWriteHelper)
                .when(mMockDeps)
                .newPersistableBundleLockingReadWriteHelper(any());
        doReturn(mLocationPermissionChecker)
                .when(mMockDeps)
                .newLocationPermissionChecker(eq(mMockContext));

        // Setup VCN instance generation
        doAnswer((invocation) -> {
@@ -712,14 +721,13 @@ public class VcnManagementServiceTest {
        verify(mMockPolicyListener).onPolicyChanged();
    }

    @Test
    public void testVcnSafeModeCallbackOnEnteredSafeMode() throws Exception {
        TelephonySubscriptionSnapshot snapshot =
                triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
    private void verifyVcnSafeModeCallback(
            @NonNull ParcelUuid subGroup, @NonNull TelephonySubscriptionSnapshot snapshot)
            throws Exception {
        verify(mMockDeps)
                .newVcn(
                        eq(mVcnContext),
                        eq(TEST_UUID_1),
                        eq(subGroup),
                        eq(TEST_VCN_CONFIG),
                        eq(snapshot),
                        mSafeModeCallbackCaptor.capture());
@@ -729,10 +737,79 @@ public class VcnManagementServiceTest {
        VcnSafeModeCallback safeModeCallback = mSafeModeCallbackCaptor.getValue();
        safeModeCallback.onEnteredSafeMode();

        assertFalse(mVcnMgmtSvc.getAllVcns().get(TEST_UUID_1).isActive());
        verify(mMockPolicyListener).onPolicyChanged();
    }

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

        verifyVcnSafeModeCallback(TEST_UUID_1, snapshot);
    }

    private void triggerVcnStatusCallbackOnEnteredSafeMode(
            @NonNull ParcelUuid subGroup,
            @NonNull String pkgName,
            int uid,
            boolean hasPermissionsforSubGroup,
            boolean hasLocationPermission)
            throws Exception {
        TelephonySubscriptionSnapshot snapshot =
                triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(subGroup));

        doReturn(hasPermissionsforSubGroup)
                .when(snapshot)
                .packageHasPermissionsForSubscriptionGroup(eq(subGroup), eq(pkgName));

        doReturn(hasLocationPermission)
                .when(mLocationPermissionChecker)
                .checkLocationPermission(eq(pkgName), any(), eq(uid), any());

        mVcnMgmtSvc.registerVcnStatusCallback(subGroup, mMockStatusCallback, pkgName);

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

        verifyVcnSafeModeCallback(subGroup, snapshot);
    }

    @Test
    public void testVcnStatusCallbackOnEnteredSafeModeWithCarrierPrivileges() throws Exception {
        triggerVcnStatusCallbackOnEnteredSafeMode(
                TEST_UUID_1,
                TEST_PACKAGE_NAME,
                TEST_UID,
                true /* hasPermissionsforSubGroup */,
                true /* hasLocationPermission */);

        verify(mMockStatusCallback, times(1)).onEnteredSafeMode();
    }

    @Test
    public void testVcnStatusCallbackOnEnteredSafeModeWithoutCarrierPrivileges() throws Exception {
        triggerVcnStatusCallbackOnEnteredSafeMode(
                TEST_UUID_1,
                TEST_PACKAGE_NAME,
                TEST_UID,
                false /* hasPermissionsforSubGroup */,
                true /* hasLocationPermission */);

        verify(mMockStatusCallback, never()).onEnteredSafeMode();
    }

    @Test
    public void testVcnStatusCallbackOnEnteredSafeModeWithoutLocationPermission() throws Exception {
        triggerVcnStatusCallbackOnEnteredSafeMode(
                TEST_UUID_1,
                TEST_PACKAGE_NAME,
                TEST_UID,
                true /* hasPermissionsforSubGroup */,
                false /* hasLocationPermission */);

        verify(mMockStatusCallback, never()).onEnteredSafeMode();
    }

    @Test
    public void testRegisterVcnStatusCallback() throws Exception {
        mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_1, mMockStatusCallback, TEST_PACKAGE_NAME);
+1 −0
Original line number Diff line number Diff line
@@ -174,6 +174,7 @@ public class VcnTest {
        statusCallback.onEnteredSafeMode();
        mTestLooper.dispatchAll();

        assertFalse(mVcn.isActive());
        for (final VcnGatewayConnection gatewayConnection : gatewayConnections) {
            verify(gatewayConnection).teardownAsynchronously();
        }