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

Commit 725dcd81 authored by Benedict Wong's avatar Benedict Wong Committed by Automerger Merge Worker
Browse files

Disable INTERNET/DUN when mobile data toggled off am: 061c84ba

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1670525

Change-Id: I5a1482d06a92c74769f9b0f7c840cb2cc983edd9
parents e388cc5d 061c84ba
Loading
Loading
Loading
Loading
+148 −4
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.server.vcn;

import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
@@ -26,14 +28,20 @@ import static com.android.server.VcnManagementService.VDBG;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.Uri;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnManager.VcnErrorCode;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelUuid;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.ArraySet;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
@@ -42,9 +50,11 @@ import com.android.internal.util.IndentingPrintWriter;
import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
@@ -61,6 +71,9 @@ import java.util.Set;
public class Vcn extends Handler {
    private static final String TAG = Vcn.class.getSimpleName();

    private static final List<Integer> CAPS_REQUIRING_MOBILE_DATA =
            Arrays.asList(NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN);

    private static final int MSG_EVENT_BASE = 0;
    private static final int MSG_CMD_BASE = 100;

@@ -110,6 +123,15 @@ public class Vcn extends Handler {
     */
    private static final int MSG_EVENT_SAFE_MODE_STATE_CHANGED = MSG_EVENT_BASE + 4;

    /**
     * Triggers reevaluation of mobile data enabled conditions.
     *
     * <p>Upon this notification, the VCN will check if any of the underlying subIds have mobile
     * data enabled. If not, the VCN will restart any GatewayConnections providing INTERNET or DUN
     * with the current mobile data toggle status.
     */
    private static final int MSG_EVENT_MOBILE_DATA_TOGGLED = MSG_EVENT_BASE + 5;

    /** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */
    private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE;

@@ -118,6 +140,8 @@ public class Vcn extends Handler {
    @NonNull private final Dependencies mDeps;
    @NonNull private final VcnNetworkRequestListener mRequestListener;
    @NonNull private final VcnCallback mVcnCallback;
    @NonNull private final VcnContentResolver mContentResolver;
    @NonNull private final ContentObserver mMobileDataSettingsObserver;

    /**
     * Map containing all VcnGatewayConnections and their VcnGatewayConnectionConfigs.
@@ -154,6 +178,8 @@ public class Vcn extends Handler {
    // Accessed from different threads, but always under lock in VcnManagementService
    private volatile int mCurrentStatus = VCN_STATUS_CODE_ACTIVE;

    private boolean mIsMobileDataEnabled = false;

    public Vcn(
            @NonNull VcnContext vcnContext,
            @NonNull ParcelUuid subscriptionGroup,
@@ -177,10 +203,19 @@ public class Vcn extends Handler {
        mVcnCallback = Objects.requireNonNull(vcnCallback, "Missing vcnCallback");
        mDeps = Objects.requireNonNull(deps, "Missing deps");
        mRequestListener = new VcnNetworkRequestListener();
        mContentResolver = mDeps.newVcnContentResolver(mVcnContext);
        mMobileDataSettingsObserver = new VcnMobileDataContentObserver(this /* handler */);

        final Uri uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA);
        mContentResolver.registerContentObserver(
                uri, true /* notifyForDescendants */, mMobileDataSettingsObserver);

        mConfig = Objects.requireNonNull(config, "Missing config");
        mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");

        // Update mIsMobileDataEnabled before starting handling of NetworkRequests.
        mIsMobileDataEnabled = getMobileDataStatus();

        // Register to receive cached and future NetworkRequests
        mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener);
    }
@@ -260,6 +295,9 @@ public class Vcn extends Handler {
            case MSG_EVENT_SAFE_MODE_STATE_CHANGED:
                handleSafeModeStatusChanged();
                break;
            case MSG_EVENT_MOBILE_DATA_TOGGLED:
                handleMobileDataToggled();
                break;
            case MSG_CMD_TEARDOWN:
                handleTeardown();
                break;
@@ -366,18 +404,37 @@ public class Vcn extends Handler {
            if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
                Slog.v(getLogTag(), "Bringing up new VcnGatewayConnection for request " + request);

                if (getExposedCapabilitiesForMobileDataState(gatewayConnectionConfig).isEmpty()) {
                    // Skip; this network does not provide any services if mobile data is disabled.
                    continue;
                }

                final VcnGatewayConnection vcnGatewayConnection =
                        mDeps.newVcnGatewayConnection(
                                mVcnContext,
                                mSubscriptionGroup,
                                mLastSnapshot,
                                gatewayConnectionConfig,
                                new VcnGatewayStatusCallbackImpl(gatewayConnectionConfig));
                                new VcnGatewayStatusCallbackImpl(gatewayConnectionConfig),
                                mIsMobileDataEnabled);
                mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection);
            }
        }
    }

    private Set<Integer> getExposedCapabilitiesForMobileDataState(
            VcnGatewayConnectionConfig gatewayConnectionConfig) {
        if (mIsMobileDataEnabled) {
            return gatewayConnectionConfig.getAllExposedCapabilities();
        }

        final Set<Integer> exposedCapsWithoutMobileData =
                new ArraySet<>(gatewayConnectionConfig.getAllExposedCapabilities());
        exposedCapsWithoutMobileData.removeAll(CAPS_REQUIRING_MOBILE_DATA);

        return exposedCapsWithoutMobileData;
    }

    private void handleGatewayConnectionQuit(VcnGatewayConnectionConfig config) {
        Slog.v(getLogTag(), "VcnGatewayConnection quit: " + config);
        mVcnGatewayConnections.remove(config);
@@ -396,12 +453,55 @@ public class Vcn extends Handler {
        }
    }

    private void handleMobileDataToggled() {
        final boolean oldMobileDataEnabledStatus = mIsMobileDataEnabled;
        mIsMobileDataEnabled = getMobileDataStatus();

        if (oldMobileDataEnabledStatus != mIsMobileDataEnabled) {
            // Teardown any GatewayConnections that advertise INTERNET or DUN. If they provide other
            // services, the VcnGatewayConnections will be restarted without advertising INTERNET or
            // DUN.
            for (Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry :
                    mVcnGatewayConnections.entrySet()) {
                final VcnGatewayConnectionConfig gatewayConnectionConfig = entry.getKey();
                final VcnGatewayConnection gatewayConnection = entry.getValue();

                final Set<Integer> exposedCaps =
                        gatewayConnectionConfig.getAllExposedCapabilities();
                if (exposedCaps.contains(NET_CAPABILITY_INTERNET)
                        || exposedCaps.contains(NET_CAPABILITY_DUN)) {
                    if (gatewayConnection == null) {
                        Slog.wtf(
                                getLogTag(),
                                "Found gatewayConnectionConfig without GatewayConnection");
                    } else {
                        // TODO(b/184868850): Optimize by restarting NetworkAgents without teardown.
                        gatewayConnection.teardownAsynchronously();
                    }
                }
            }
        }
    }

    private boolean getMobileDataStatus() {
        final TelephonyManager genericTelMan =
                mVcnContext.getContext().getSystemService(TelephonyManager.class);

        for (int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) {
            if (genericTelMan.createForSubscriptionId(subId).isDataEnabled()) {
                return true;
            }
        }

        return false;
    }

    private boolean isRequestSatisfiedByGatewayConnectionConfig(
            @NonNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config) {
        final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
        builder.addTransportType(TRANSPORT_CELLULAR);
        builder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
        for (int cap : config.getAllExposedCapabilities()) {
        for (int cap : getExposedCapabilitiesForMobileDataState(config)) {
            builder.addCapability(cap);
        }

@@ -432,6 +532,16 @@ public class Vcn extends Handler {
        pw.decreaseIndent();
    }

    @VisibleForTesting(visibility = Visibility.PRIVATE)
    public boolean isMobileDataEnabled() {
        return mIsMobileDataEnabled;
    }

    @VisibleForTesting(visibility = Visibility.PRIVATE)
    public void setMobileDataEnabled(boolean isMobileDataEnabled) {
        mIsMobileDataEnabled = isMobileDataEnabled;
    }

    /** Retrieves the network score for a VCN Network */
    // Package visibility for use in VcnGatewayConnection
    static int getNetworkScore() {
@@ -485,6 +595,17 @@ public class Vcn extends Handler {
        }
    }

    private class VcnMobileDataContentObserver extends ContentObserver {
        private VcnMobileDataContentObserver(Handler handler) {
            super(handler);
        }

        @Override
        public void onChange(boolean selfChange) {
            sendMessage(obtainMessage(MSG_EVENT_MOBILE_DATA_TOGGLED));
        }
    }

    /** External dependencies used by Vcn, for injection in tests */
    @VisibleForTesting(visibility = Visibility.PRIVATE)
    public static class Dependencies {
@@ -494,13 +615,36 @@ public class Vcn extends Handler {
                ParcelUuid subscriptionGroup,
                TelephonySubscriptionSnapshot snapshot,
                VcnGatewayConnectionConfig connectionConfig,
                VcnGatewayStatusCallback gatewayStatusCallback) {
                VcnGatewayStatusCallback gatewayStatusCallback,
                boolean isMobileDataEnabled) {
            return new VcnGatewayConnection(
                    vcnContext,
                    subscriptionGroup,
                    snapshot,
                    connectionConfig,
                    gatewayStatusCallback);
                    gatewayStatusCallback,
                    isMobileDataEnabled);
        }

        /** Builds a new VcnContentResolver instance */
        public VcnContentResolver newVcnContentResolver(VcnContext vcnContext) {
            return new VcnContentResolver(vcnContext);
        }
    }

    /** Proxy Implementation of NetworkAgent, used for testing. */
    @VisibleForTesting(visibility = Visibility.PRIVATE)
    public static class VcnContentResolver {
        private final ContentResolver mImpl;

        public VcnContentResolver(VcnContext vcnContext) {
            mImpl = vcnContext.getContext().getContentResolver();
        }

        /** Registers the content observer */
        public void registerContentObserver(
                @NonNull Uri uri, boolean notifyForDescendants, @NonNull ContentObserver observer) {
            mImpl.registerContentObserver(uri, notifyForDescendants, observer);
        }
    }
}
+18 −4
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.server.vcn;

import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
@@ -517,6 +519,7 @@ public class VcnGatewayConnection extends StateMachine {
    @NonNull private final VcnGatewayStatusCallback mGatewayStatusCallback;
    @NonNull private final Dependencies mDeps;
    @NonNull private final VcnUnderlyingNetworkTrackerCallback mUnderlyingNetworkTrackerCallback;
    private final boolean mIsMobileDataEnabled;

    @NonNull private final IpSecManager mIpSecManager;

@@ -626,13 +629,15 @@ public class VcnGatewayConnection extends StateMachine {
            @NonNull ParcelUuid subscriptionGroup,
            @NonNull TelephonySubscriptionSnapshot snapshot,
            @NonNull VcnGatewayConnectionConfig connectionConfig,
            @NonNull VcnGatewayStatusCallback gatewayStatusCallback) {
            @NonNull VcnGatewayStatusCallback gatewayStatusCallback,
            boolean isMobileDataEnabled) {
        this(
                vcnContext,
                subscriptionGroup,
                snapshot,
                connectionConfig,
                gatewayStatusCallback,
                isMobileDataEnabled,
                new Dependencies());
    }

@@ -643,6 +648,7 @@ public class VcnGatewayConnection extends StateMachine {
            @NonNull TelephonySubscriptionSnapshot snapshot,
            @NonNull VcnGatewayConnectionConfig connectionConfig,
            @NonNull VcnGatewayStatusCallback gatewayStatusCallback,
            boolean isMobileDataEnabled,
            @NonNull Dependencies deps) {
        super(TAG, Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
        mVcnContext = vcnContext;
@@ -650,6 +656,7 @@ public class VcnGatewayConnection extends StateMachine {
        mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig");
        mGatewayStatusCallback =
                Objects.requireNonNull(gatewayStatusCallback, "Missing gatewayStatusCallback");
        mIsMobileDataEnabled = isMobileDataEnabled;
        mDeps = Objects.requireNonNull(deps, "Missing deps");

        mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
@@ -1502,7 +1509,7 @@ public class VcnGatewayConnection extends StateMachine {
                @NonNull VcnNetworkAgent agent,
                @NonNull VcnChildSessionConfiguration childConfig) {
            final NetworkCapabilities caps =
                    buildNetworkCapabilities(mConnectionConfig, mUnderlying);
                    buildNetworkCapabilities(mConnectionConfig, mUnderlying, mIsMobileDataEnabled);
            final LinkProperties lp =
                    buildConnectedLinkProperties(
                            mConnectionConfig, tunnelIface, childConfig, mUnderlying);
@@ -1515,7 +1522,7 @@ public class VcnGatewayConnection extends StateMachine {
                @NonNull IpSecTunnelInterface tunnelIface,
                @NonNull VcnChildSessionConfiguration childConfig) {
            final NetworkCapabilities caps =
                    buildNetworkCapabilities(mConnectionConfig, mUnderlying);
                    buildNetworkCapabilities(mConnectionConfig, mUnderlying, mIsMobileDataEnabled);
            final LinkProperties lp =
                    buildConnectedLinkProperties(
                            mConnectionConfig, tunnelIface, childConfig, mUnderlying);
@@ -1843,7 +1850,8 @@ public class VcnGatewayConnection extends StateMachine {
    @VisibleForTesting(visibility = Visibility.PRIVATE)
    static NetworkCapabilities buildNetworkCapabilities(
            @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig,
            @Nullable UnderlyingNetworkRecord underlying) {
            @Nullable UnderlyingNetworkRecord underlying,
            boolean isMobileDataEnabled) {
        final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();

        builder.addTransportType(TRANSPORT_CELLULAR);
@@ -1853,6 +1861,12 @@ public class VcnGatewayConnection extends StateMachine {

        // Add exposed capabilities
        for (int cap : gatewayConnectionConfig.getAllExposedCapabilities()) {
            // Skip adding INTERNET or DUN if mobile data is disabled.
            if (!isMobileDataEnabled
                    && (cap == NET_CAPABILITY_INTERNET || cap == NET_CAPABILITY_DUN)) {
                continue;
            }

            builder.addCapability(cap);
        }

+1 −0
Original line number Diff line number Diff line
@@ -65,6 +65,7 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
                        TEST_SUBSCRIPTION_SNAPSHOT,
                        mConfig,
                        mGatewayStatusCallback,
                        true /* isMobileDataEnabled */,
                        mDeps);

        vgc.setIsQuitting(true);
+24 −4
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.server.vcn;

import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
@@ -89,7 +91,8 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
        doReturn(mWifiInfo).when(mWifiInfo).makeCopy(anyLong());
    }

    private void verifyBuildNetworkCapabilitiesCommon(int transportType) {
    private void verifyBuildNetworkCapabilitiesCommon(
            int transportType, boolean isMobileDataEnabled) {
        final NetworkCapabilities.Builder capBuilder = new NetworkCapabilities.Builder();
        capBuilder.addTransportType(transportType);
        capBuilder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
@@ -109,10 +112,22 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
                capBuilder.build(), new LinkProperties(), false);
        final NetworkCapabilities vcnCaps =
                VcnGatewayConnection.buildNetworkCapabilities(
                        VcnGatewayConnectionConfigTest.buildTestConfig(), record);
                        VcnGatewayConnectionConfigTest.buildTestConfig(),
                        record,
                        isMobileDataEnabled);

        assertTrue(vcnCaps.hasTransport(TRANSPORT_CELLULAR));
        assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_METERED));
        assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING));

        for (int cap : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
            if (cap == NET_CAPABILITY_INTERNET || cap == NET_CAPABILITY_DUN) {
                assertEquals(isMobileDataEnabled, vcnCaps.hasCapability(cap));
            } else {
                assertTrue(vcnCaps.hasCapability(cap));
            }
        }

        assertArrayEquals(new int[] {TEST_UID}, vcnCaps.getAdministratorUids());
        assertTrue(vcnCaps.getTransportInfo() instanceof VcnTransportInfo);

@@ -126,12 +141,17 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {

    @Test
    public void testBuildNetworkCapabilitiesUnderlyingWifi() throws Exception {
        verifyBuildNetworkCapabilitiesCommon(TRANSPORT_WIFI);
        verifyBuildNetworkCapabilitiesCommon(TRANSPORT_WIFI, true /* isMobileDataEnabled */);
    }

    @Test
    public void testBuildNetworkCapabilitiesUnderlyingCell() throws Exception {
        verifyBuildNetworkCapabilitiesCommon(TRANSPORT_CELLULAR);
        verifyBuildNetworkCapabilitiesCommon(TRANSPORT_CELLULAR, true /* isMobileDataEnabled */);
    }

    @Test
    public void testBuildNetworkCapabilitiesMobileDataDisabled() throws Exception {
        verifyBuildNetworkCapabilitiesCommon(TRANSPORT_CELLULAR, false /* isMobileDataEnabled */);
    }

    @Test
+1 −0
Original line number Diff line number Diff line
@@ -202,6 +202,7 @@ public class VcnGatewayConnectionTestBase {
                        TEST_SUBSCRIPTION_SNAPSHOT,
                        mConfig,
                        mGatewayStatusCallback,
                        true /* isMobileDataEnabled */,
                        mDeps);
    }

Loading