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

Commit 9cb58078 authored by Benedict Wong's avatar Benedict Wong
Browse files

Verify carrier privileges for VCN-providing packages

This change adds and verifies packages in the VcnManagmentService,
ensuring that carrier privilege gain/loss correctly starts/tears down
VCN instances.

Bug: 163431877
Test: atest FrameworksNetTests
Change-Id: I63203188c57fdde1cfc58aaf1108aa3e70eb4a50
parent 6153723d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -23,6 +23,6 @@ import android.os.ParcelUuid;
 * @hide
 */
interface IVcnManagementService {
    void setVcnConfig(in ParcelUuid subscriptionGroup, in VcnConfig config);
    void setVcnConfig(in ParcelUuid subscriptionGroup, in VcnConfig config, in String opPkgName);
    void clearVcnConfig(in ParcelUuid subscriptionGroup);
}
+37 −6
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ import static com.android.internal.annotations.VisibleForTesting.Visibility;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
@@ -45,11 +46,17 @@ import java.util.Set;
public final class VcnConfig implements Parcelable {
    @NonNull private static final String TAG = VcnConfig.class.getSimpleName();

    private static final String PACKAGE_NAME_KEY = "mPackageName";
    @NonNull private final String mPackageName;

    private static final String GATEWAY_CONNECTION_CONFIGS_KEY = "mGatewayConnectionConfigs";
    @NonNull private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs;

    private VcnConfig(@NonNull Set<VcnGatewayConnectionConfig> tunnelConfigs) {
        mGatewayConnectionConfigs = Collections.unmodifiableSet(tunnelConfigs);
    private VcnConfig(
            @NonNull String packageName,
            @NonNull Set<VcnGatewayConnectionConfig> gatewayConnectionConfigs) {
        mPackageName = packageName;
        mGatewayConnectionConfigs = Collections.unmodifiableSet(gatewayConnectionConfigs);

        validate();
    }
@@ -61,6 +68,8 @@ public final class VcnConfig implements Parcelable {
     */
    @VisibleForTesting(visibility = Visibility.PRIVATE)
    public VcnConfig(@NonNull PersistableBundle in) {
        mPackageName = in.getString(PACKAGE_NAME_KEY);

        final PersistableBundle gatewayConnectionConfigsBundle =
                in.getPersistableBundle(GATEWAY_CONNECTION_CONFIGS_KEY);
        mGatewayConnectionConfigs =
@@ -72,8 +81,19 @@ public final class VcnConfig implements Parcelable {
    }

    private void validate() {
        Objects.requireNonNull(mPackageName, "packageName was null");
        Preconditions.checkCollectionNotEmpty(
                mGatewayConnectionConfigs, "gatewayConnectionConfigs");
                mGatewayConnectionConfigs, "gatewayConnectionConfigs was empty");
    }

    /**
     * Retrieve the package name of the provisioning app.
     *
     * @hide
     */
    @NonNull
    public String getProvisioningPackageName() {
        return mPackageName;
    }

    /** Retrieves the set of configured tunnels. */
@@ -91,6 +111,8 @@ public final class VcnConfig implements Parcelable {
    public PersistableBundle toPersistableBundle() {
        final PersistableBundle result = new PersistableBundle();

        result.putString(PACKAGE_NAME_KEY, mPackageName);

        final PersistableBundle gatewayConnectionConfigsBundle =
                PersistableBundleUtils.fromList(
                        new ArrayList<>(mGatewayConnectionConfigs),
@@ -102,7 +124,7 @@ public final class VcnConfig implements Parcelable {

    @Override
    public int hashCode() {
        return Objects.hash(mGatewayConnectionConfigs);
        return Objects.hash(mPackageName, mGatewayConnectionConfigs);
    }

    @Override
@@ -112,7 +134,8 @@ public final class VcnConfig implements Parcelable {
        }

        final VcnConfig rhs = (VcnConfig) other;
        return mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs);
        return mPackageName.equals(rhs.mPackageName)
                && mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs);
    }

    // Parcelable methods
@@ -143,9 +166,17 @@ public final class VcnConfig implements Parcelable {

    /** This class is used to incrementally build {@link VcnConfig} objects. */
    public static class Builder {
        @NonNull private final String mPackageName;

        @NonNull
        private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs = new ArraySet<>();

        public Builder(@NonNull Context context) {
            Objects.requireNonNull(context, "context was null");

            mPackageName = context.getOpPackageName();
        }

        /**
         * Adds a configuration for an individual gateway connection.
         *
@@ -168,7 +199,7 @@ public final class VcnConfig implements Parcelable {
         */
        @NonNull
        public VcnConfig build() {
            return new VcnConfig(mGatewayConnectionConfigs);
            return new VcnConfig(mPackageName, mGatewayConnectionConfigs);
        }
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -101,7 +101,7 @@ public final class VcnManager {
        requireNonNull(config, "config was null");

        try {
            mService.setVcnConfig(subscriptionGroup, config);
            mService.setVcnConfig(subscriptionGroup, config, mContext.getOpPackageName());
        } catch (ServiceSpecificException e) {
            throw new IOException(e);
        } catch (RemoteException e) {
+18 −5
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.app.AppOpsManager;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkProvider;
@@ -122,6 +123,7 @@ public class VcnManagementService extends IVcnManagementService.Stub {
    @VisibleForTesting(visibility = Visibility.PRIVATE)
    static final String VCN_CONFIG_FILE = "/data/system/vcn/configs.xml";

    // TODO(b/176956496): Directly use CarrierServiceBindHelper.UNBIND_DELAY_MILLIS
    @VisibleForTesting(visibility = Visibility.PRIVATE)
    static final long CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS = TimeUnit.SECONDS.toMillis(30);

@@ -346,9 +348,8 @@ public class VcnManagementService extends IVcnManagementService.Stub {

                // Start any VCN instances as necessary
                for (Entry<ParcelUuid, VcnConfig> entry : mConfigs.entrySet()) {
                    if (snapshot.getActiveSubscriptionGroups().contains(entry.getKey())) {
                        // TODO: Add checks to ensure provisioning app is currently carrier
                        //       privileged on this subscription
                    if (snapshot.packageHasPermissionsForSubscriptionGroup(
                            entry.getKey(), entry.getValue().getProvisioningPackageName())) {
                        if (!mVcns.containsKey(entry.getKey())) {
                            startVcnLocked(entry.getKey(), entry.getValue());
                        }
@@ -361,7 +362,10 @@ public class VcnManagementService extends IVcnManagementService.Stub {
                // Schedule teardown of any VCN instances that have lost carrier privileges (after a
                // delay)
                for (Entry<ParcelUuid, Vcn> entry : mVcns.entrySet()) {
                    if (!snapshot.getActiveSubscriptionGroups().contains(entry.getKey())) {
                    final VcnConfig config = mConfigs.get(entry.getKey());
                    if (config == null
                            || !snapshot.packageHasPermissionsForSubscriptionGroup(
                                    entry.getKey(), config.getProvisioningPackageName())) {
                        final ParcelUuid uuidToTeardown = entry.getKey();
                        final Vcn instanceToTeardown = entry.getValue();

@@ -408,11 +412,20 @@ public class VcnManagementService extends IVcnManagementService.Stub {
     * <p>Implements the IVcnManagementService Binder interface.
     */
    @Override
    public void setVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) {
    public void setVcnConfig(
            @NonNull ParcelUuid subscriptionGroup,
            @NonNull VcnConfig config,
            @NonNull String opPkgName) {
        requireNonNull(subscriptionGroup, "subscriptionGroup was null");
        requireNonNull(config, "config was null");
        requireNonNull(opPkgName, "opPkgName was null");
        if (!config.getProvisioningPackageName().equals(opPkgName)) {
            throw new IllegalArgumentException("Mismatched caller and VcnConfig creator");
        }
        Slog.v(TAG, "VCN config updated for subGrp: " + subscriptionGroup);

        mContext.getSystemService(AppOpsManager.class)
                .checkPackage(mDeps.getBinderCallingUid(), config.getProvisioningPackageName());
        enforceCallingUserAndCarrierPrivilege(subscriptionGroup);

        Binder.withCleanCallingIdentity(() -> {
+50 −13
Original line number Diff line number Diff line
@@ -36,6 +36,8 @@ import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;

@@ -79,6 +81,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
    @NonNull private final TelephonySubscriptionTrackerCallback mCallback;
    @NonNull private final Dependencies mDeps;

    @NonNull private final TelephonyManager mTelephonyManager;
    @NonNull private final SubscriptionManager mSubscriptionManager;
    @NonNull private final CarrierConfigManager mCarrierConfigManager;

@@ -106,6 +109,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
        mCallback = Objects.requireNonNull(callback, "Missing callback");
        mDeps = Objects.requireNonNull(deps, "Missing deps");

        mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
        mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
        mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);

@@ -139,7 +143,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
     * so callbacks & broadcasts are all serialized on mHandler, avoiding the need for locking.
     */
    public void handleSubscriptionsChanged() {
        final Set<ParcelUuid> activeSubGroups = new ArraySet<>();
        final Map<ParcelUuid, Set<String>> privilegedPackages = new HashMap<>();
        final Map<Integer, ParcelUuid> newSubIdToGroupMap = new HashMap<>();

        final List<SubscriptionInfo> allSubs = mSubscriptionManager.getAllSubscriptionInfoList();
@@ -166,12 +170,22 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
            // group.
            if (subInfo.getSimSlotIndex() != INVALID_SIM_SLOT_INDEX
                    && mReadySubIdsBySlotId.values().contains(subInfo.getSubscriptionId())) {
                activeSubGroups.add(subInfo.getGroupUuid());
                // TODO (b/172619301): Cache based on callbacks from CarrierPrivilegesTracker

                final TelephonyManager subIdSpecificTelephonyManager =
                        mTelephonyManager.createForSubscriptionId(subInfo.getSubscriptionId());

                final ParcelUuid subGroup = subInfo.getGroupUuid();
                final Set<String> pkgs =
                        privilegedPackages.getOrDefault(subGroup, new ArraySet<>());
                pkgs.addAll(subIdSpecificTelephonyManager.getPackagesWithCarrierPrivileges());

                privilegedPackages.put(subGroup, pkgs);
            }
        }

        final TelephonySubscriptionSnapshot newSnapshot =
                new TelephonySubscriptionSnapshot(newSubIdToGroupMap, activeSubGroups);
                new TelephonySubscriptionSnapshot(newSubIdToGroupMap, privilegedPackages);

        // If snapshot was meaningfully updated, fire the callback
        if (!newSnapshot.equals(mCurrentSnapshot)) {
@@ -231,25 +245,40 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
    /** TelephonySubscriptionSnapshot is a class containing info about active subscriptions */
    public static class TelephonySubscriptionSnapshot {
        private final Map<Integer, ParcelUuid> mSubIdToGroupMap;
        private final Set<ParcelUuid> mActiveGroups;
        private final Map<ParcelUuid, Set<String>> mPrivilegedPackages;

        public static final TelephonySubscriptionSnapshot EMPTY_SNAPSHOT =
                new TelephonySubscriptionSnapshot(Collections.emptyMap(), Collections.emptySet());
                new TelephonySubscriptionSnapshot(Collections.emptyMap(), Collections.emptyMap());

        @VisibleForTesting(visibility = Visibility.PRIVATE)
        TelephonySubscriptionSnapshot(
                @NonNull Map<Integer, ParcelUuid> subIdToGroupMap,
                @NonNull Set<ParcelUuid> activeGroups) {
            mSubIdToGroupMap = Collections.unmodifiableMap(
                    Objects.requireNonNull(subIdToGroupMap, "subIdToGroupMap was null"));
            mActiveGroups = Collections.unmodifiableSet(
                    Objects.requireNonNull(activeGroups, "activeGroups was null"));
                @NonNull Map<ParcelUuid, Set<String>> privilegedPackages) {
            Objects.requireNonNull(subIdToGroupMap, "subIdToGroupMap was null");
            Objects.requireNonNull(privilegedPackages, "privilegedPackages was null");

            mSubIdToGroupMap = Collections.unmodifiableMap(subIdToGroupMap);

            final Map<ParcelUuid, Set<String>> unmodifiableInnerSets = new ArrayMap<>();
            for (Entry<ParcelUuid, Set<String>> entry : privilegedPackages.entrySet()) {
                unmodifiableInnerSets.put(
                        entry.getKey(), Collections.unmodifiableSet(entry.getValue()));
            }
            mPrivilegedPackages = Collections.unmodifiableMap(unmodifiableInnerSets);
        }

        /** Returns the active subscription groups */
        @NonNull
        public Set<ParcelUuid> getActiveSubscriptionGroups() {
            return mActiveGroups;
            return mPrivilegedPackages.keySet();
        }

        /** Checks if the provided package is carrier privileged for the specified sub group. */
        public boolean packageHasPermissionsForSubscriptionGroup(
                @NonNull ParcelUuid subGrp, @NonNull String packageName) {
            final Set<String> privilegedPackages = mPrivilegedPackages.get(subGrp);

            return privilegedPackages != null && privilegedPackages.contains(packageName);
        }

        /** Returns the Subscription Group for a given subId. */
@@ -276,7 +305,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {

        @Override
        public int hashCode() {
            return Objects.hash(mSubIdToGroupMap, mActiveGroups);
            return Objects.hash(mSubIdToGroupMap, mPrivilegedPackages);
        }

        @Override
@@ -288,7 +317,15 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
            final TelephonySubscriptionSnapshot other = (TelephonySubscriptionSnapshot) obj;

            return mSubIdToGroupMap.equals(other.mSubIdToGroupMap)
                    && mActiveGroups.equals(other.mActiveGroups);
                    && mPrivilegedPackages.equals(other.mPrivilegedPackages);
        }

        @Override
        public String toString() {
            return "TelephonySubscriptionSnapshot{ "
                    + "mSubIdToGroupMap=" + mSubIdToGroupMap
                    + ", mPrivilegedPackages=" + mPrivilegedPackages
                    + " }";
        }
    }

Loading