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

Commit 572ba60b authored by Jack Yu's avatar Jack Yu Committed by Sarah Chin
Browse files

Fixed unmetered network request caused data leak

For some carriers, MMS (unmetered) and internet (metered)
is sharing the same APN. When the user sends MMS
when data is turned off, internet will become available
after sending the MMS. That is because connectivity
service sees there are lots of internet requests
that can be satisfied by this network, so the tear
down request will not be sent from connectivity service
after MMS is sent.

When unmetered request brings up the network that
also supports metered usage, we should remove those
metered capabilities. This can make sure this network
can be only used for unmetered purposes.

Also fixed a bug that MMS-always-allowed feature should
be only applicable to data disabled case, not roaming
disabled case.

Fix: 220171983
Test: Manual & atest DataNetworkControllerTest
Change-Id: I30d781c45b7a1c89646ec6d168928e5289dc5575
Merged-In: I30d781c45b7a1c89646ec6d168928e5289dc5575
parent 84536c72
Loading
Loading
Loading
Loading
+9 −2
Original line number Diff line number Diff line
@@ -35,13 +35,13 @@ import java.util.Set;
 */
public class DataEvaluation {
    /** The reason for this evaluation */
    private final DataEvaluationReason mDataEvaluationReason;
    private final @NonNull DataEvaluationReason mDataEvaluationReason;

    /** Data disallowed reasons. There could be multiple reasons for not allowing data. */
    private final @NonNull Set<DataDisallowedReason> mDataDisallowedReasons = new HashSet<>();

    /** Data allowed reason. It is intended to only have one allowed reason. */
    private DataAllowedReason mDataAllowedReason = DataAllowedReason.NONE;
    private @NonNull DataAllowedReason mDataAllowedReason = DataAllowedReason.NONE;

    private @Nullable DataProfile mCandidateDataProfile = null;

@@ -103,6 +103,13 @@ public class DataEvaluation {
        return new ArrayList<>(mDataDisallowedReasons);
    }

    /**
     * @return The data allowed reason.
     */
    public @NonNull DataAllowedReason getDataAllowedReason() {
        return mDataAllowedReason;
    }

    /**
     * Set the candidate data profile for setup data network.
     *
+46 −7
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@ import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.data.DataEvaluation.DataAllowedReason;
import com.android.internal.telephony.data.DataNetworkController.NetworkRequestList;
import com.android.internal.telephony.data.DataRetryManager.DataHandoverRetryEntry;
import com.android.internal.telephony.data.DataRetryManager.DataRetryEntry;
@@ -504,6 +505,9 @@ public class DataNetwork extends StateMachine {
     */
    private @TransportType int mTransport;

    /** The reason that why setting up this data network is allowed. */
    private @NonNull DataAllowedReason mDataAllowedReason;

    /**
     * PCO (Protocol Configuration Options) data received from the network. Key is the PCO id, value
     * is the PCO content.
@@ -677,6 +681,7 @@ public class DataNetwork extends StateMachine {
     * @param dataProfile The data profile for establishing the data network.
     * @param networkRequestList The initial network requests attached to this data network.
     * @param transport The initial transport of the data network.
     * @param dataAllowedReason The reason that why setting up this data network is allowed.
     * @param callback The callback to receives data network state update.
     */
    public DataNetwork(@NonNull Phone phone, @NonNull Looper looper,
@@ -684,6 +689,7 @@ public class DataNetwork extends StateMachine {
            @NonNull DataProfile dataProfile,
            @NonNull NetworkRequestList networkRequestList,
            @TransportType int transport,
            @NonNull DataAllowedReason dataAllowedReason,
            @NonNull DataNetworkCallback callback) {
        super("DataNetwork", looper);
        mPhone = phone;
@@ -704,6 +710,7 @@ public class DataNetwork extends StateMachine {
        mDataNetworkCallback = callback;
        mDataProfile = dataProfile;
        mTransport = transport;
        mDataAllowedReason = dataAllowedReason;
        dataProfile.setLastSetupTimestamp(SystemClock.elapsedRealtime());
        mAttachedNetworkRequestList.addAll(networkRequestList);
        mCid.put(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, INVALID_CID);
@@ -1392,7 +1399,6 @@ public class DataNetwork extends StateMachine {
            }
        }

        // TODO: Support NET_CAPABILITY_NOT_RESTRICTED
        if (!mCongested) {
            builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED);
        }
@@ -1416,21 +1422,54 @@ public class DataNetwork extends StateMachine {
            builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
        }

        if (NetworkCapabilitiesUtils.inferRestrictedCapability(builder.build())) {
            builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
        }

        Set<Integer> meteredCapabilities = mDataConfigManager
                .getMeteredNetworkCapabilities(roaming);
                .getMeteredNetworkCapabilities(roaming).stream()
                .filter(cap -> mAccessNetworksManager.getPreferredTransportByNetworkCapability(cap)
                        == AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
                .collect(Collectors.toSet());
        boolean unmeteredNetwork = meteredCapabilities.stream().noneMatch(
                Arrays.stream(builder.build().getCapabilities()).boxed()
                        .collect(Collectors.toSet())::contains);

        // TODO: Support NET_CAPABILITY_NOT_METERED when non-restricted data is for unmetered use
        if (unmeteredNetwork) {
            builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
        }

        // Always start with not-restricted, and then remove if needed.
        builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);

        // When data is disabled, or data roaming is disabled and the device is roaming, we need
        // to remove certain capabilities depending on scenarios.
        if (!mDataNetworkController.getDataSettingsManager().isDataEnabled()
                || (mPhone.getServiceState().getDataRoaming()
                && !mDataNetworkController.getDataSettingsManager().isDataRoamingEnabled())) {
            // If data is allowed because the request is a restricted network request, we need
            // to mark the network as restricted when data is disabled or data roaming is disabled
            // and the device is roaming. If we don't do that, non-privileged apps will be able
            // to use this network when data is disabled.
            if (mDataAllowedReason == DataAllowedReason.RESTRICTED_REQUEST) {
                builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
            } else if (mDataAllowedReason == DataAllowedReason.UNMETERED_USAGE
                    || mDataAllowedReason == DataAllowedReason.MMS_REQUEST) {
                // If data is allowed due to unmetered usage, or MMS always-allowed, we need to
                // remove unrelated-but-metered capabilities.
                for (int capability : meteredCapabilities) {
                    // 1. If it's unmetered usage, remove all metered capabilities.
                    // 2. if it's MMS always-allowed, then remove all metered capabilities but MMS.
                    if (capability != NetworkCapabilities.NET_CAPABILITY_MMS
                            || mDataAllowedReason != DataAllowedReason.MMS_REQUEST) {
                        builder.removeCapability(capability);
                    }
                }
            }
        }

        // If one of the capabilities are for special use, for example, IMS, CBS, then this
        // network should be restricted, regardless data is enabled or not.
        if (NetworkCapabilitiesUtils.inferRestrictedCapability(builder.build())) {
            builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
        }

        // Set the bandwidth information.
        builder.setLinkDownstreamBandwidthKbps(mNetworkBandwidth.downlinkBandwidthKbps);
        builder.setLinkUpstreamBandwidthKbps(mNetworkBandwidth.uplinkBandwidthKbps);
+23 −14
Original line number Diff line number Diff line
@@ -1054,7 +1054,8 @@ public class DataNetworkController extends Handler {
        if (!evaluation.containsDisallowedReasons()) {
            DataProfile dataProfile = evaluation.getCandidateDataProfile();
            if (dataProfile != null) {
                setupDataNetwork(dataProfile, null);
                setupDataNetwork(dataProfile, null,
                        evaluation.getDataAllowedReason());
            }
        }
    }
@@ -1236,7 +1237,6 @@ public class DataNetworkController extends Handler {
            evaluation.addDataDisallowedReason(DataDisallowedReason.EMERGENCY_CALL);
        }


        if (!mDataSettingsManager.isDataEnabled(DataUtils.networkCapabilityToApnType(
                networkRequest.getApnTypeNetworkCapability()))) {
            evaluation.addDataDisallowedReason(DataDisallowedReason.DATA_DISABLED);
@@ -1245,13 +1245,16 @@ public class DataNetworkController extends Handler {
        // Check whether to allow data in certain situations if data is disallowed for soft reasons
        if (!evaluation.containsDisallowedReasons()) {
            evaluation.addDataAllowedReason(DataAllowedReason.NORMAL);
        } else if (!evaluation.containsHardDisallowedReasons()) {
            // Check if request is MMS and MMS is always allowed
            if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)

            if (!mDataSettingsManager.isDataEnabled()
                    && networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
                    && mDataSettingsManager.isMmsAlwaysAllowed()) {
                // We reach here when data is disabled, but MMS always-allowed is enabled.
                // (Note that isDataEnabled(ApnSetting.TYPE_MMS) returns true in this case, so it
                // would not generate any soft disallowed reason. We need to explicitly handle it.)
                evaluation.addDataAllowedReason(DataAllowedReason.MMS_REQUEST);
            }

        } else if (!evaluation.containsHardDisallowedReasons()) {
            // Check if request is unmetered (WiFi or unmetered APN)
            if (transport == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
                evaluation.addDataAllowedReason(DataAllowedReason.UNMETERED_USAGE);
@@ -1292,7 +1295,8 @@ public class DataNetworkController extends Handler {
                + TelephonyManager.getNetworkTypeName(getDataNetworkType(transport))
                + ", reg state="
                + NetworkRegistrationInfo.registrationStateToString(
                        getDataRegistrationState(transport)));
                        getDataRegistrationState(transport))
                + ", " + networkRequest);
        return evaluation;
    }

@@ -1338,7 +1342,8 @@ public class DataNetworkController extends Handler {
            if (!evaluation.containsDisallowedReasons()) {
                DataProfile dataProfile = evaluation.getCandidateDataProfile();
                if (dataProfile != null) {
                    setupDataNetwork(dataProfile, null);
                    setupDataNetwork(dataProfile, null,
                            evaluation.getDataAllowedReason());
                }
            }
        }
@@ -1416,8 +1421,7 @@ public class DataNetworkController extends Handler {
        }

        // Check if data roaming is disabled
        if (mPhone.getServiceState().getDataRoaming()
                && !mDataSettingsManager.isDataRoamingEnabled()) {
        if (mServiceState.getDataRoaming() && !mDataSettingsManager.isDataRoamingEnabled()) {
            evaluation.addDataDisallowedReason(DataDisallowedReason.ROAMING_DISABLED);
        }

@@ -1858,11 +1862,14 @@ public class DataNetworkController extends Handler {
     * @param dataProfile The data profile to setup the data network.
     * @param dataSetupRetryEntry Data retry entry. {@code null} if this data network setup is not
     * initiated by a data retry.
     * @param allowedReason The reason that why setting up this data network is allowed.
     */
    private void setupDataNetwork(@NonNull DataProfile dataProfile,
            @Nullable DataSetupRetryEntry dataSetupRetryEntry) {
            @Nullable DataSetupRetryEntry dataSetupRetryEntry,
            @NonNull DataAllowedReason allowedReason) {
        log("onSetupDataNetwork: dataProfile=" + dataProfile + ", retryEntry="
                + dataSetupRetryEntry + ", service state=" + mServiceState);
                + dataSetupRetryEntry + ", allowed reason=" + allowedReason + ", service state="
                + mServiceState);
        for (DataNetwork dataNetwork : mDataNetworkList) {
            if (dataNetwork.getDataProfile().equals(dataProfile)) {
                log("onSetupDataNetwork: Found existing data network " + dataNetwork
@@ -1893,7 +1900,8 @@ public class DataNetworkController extends Handler {
                + ", and attaching " + networkRequestList.size() + " network requests to it.");

        mDataNetworkList.add(new DataNetwork(mPhone, getLooper(), mDataServiceManagers,
                dataProfile, networkRequestList, transport, new DataNetworkCallback(this::post) {
                dataProfile, networkRequestList, transport, allowedReason,
                new DataNetworkCallback(this::post) {
                    @Override
                    public void onSetupDataFailed(@NonNull DataNetwork dataNetwork,
                            @NonNull NetworkRequestList requestList, @DataFailureCause int cause,
@@ -2055,7 +2063,8 @@ public class DataNetworkController extends Handler {
                dataProfile = evaluation.getCandidateDataProfile();
            }
            if (dataProfile != null) {
                setupDataNetwork(dataProfile, dataSetupRetryEntry);
                setupDataNetwork(dataProfile, dataSetupRetryEntry,
                        evaluation.getDataAllowedReason());
            } else {
                loge("onDataNetworkSetupRetry: Not able to find a suitable data profile to retry.");
            }
+7 −5
Original line number Diff line number Diff line
@@ -157,7 +157,8 @@ public class TelephonyNetworkRequest {
    /**
     * Data config manager for retrieving data config.
     */
    private final @NonNull DataConfigManager mDataConfigManager;
    // TODO: Make this @NonNull after old data stack removed.
    private final @Nullable DataConfigManager mDataConfigManager;

    /**
     * The attached data network. Note that the data network could be in any state. {@code null}
@@ -387,8 +388,9 @@ public class TelephonyNetworkRequest {
     * @return {@code true} if this network request can result in bringing up a metered network.
     */
    public boolean isMeteredRequest() {
        return mDataConfigManager.isAnyMeteredCapability(getCapabilities(),
                mPhone.getServiceState().getDataRoaming());
        // TODO: Remove null check after old data stack removed.
        return mDataConfigManager != null && mDataConfigManager.isAnyMeteredCapability(
                getCapabilities(), mPhone.getServiceState().getDataRoaming());
    }

    /**
@@ -442,8 +444,8 @@ public class TelephonyNetworkRequest {
        return "[" + mNativeNetworkRequest.toString() + ", mPriority=" + mPriority
                + ", state=" + requestStateToString(mState)
                + ", mAttachedDataNetwork=" + (mAttachedDataNetwork != null
                ? mAttachedDataNetwork.name() : null) + ", created time="
                + DataUtils.elapsedTimeToString(mCreatedTimeMillis)
                ? mAttachedDataNetwork.name() : null) + ", isMetered=" + isMeteredRequest()
                + ", created time=" + DataUtils.elapsedTimeToString(mCreatedTimeMillis)
                + ", evaluation result=" + mEvaluation + "]";
    }

+2 −1
Original line number Diff line number Diff line
@@ -64,8 +64,9 @@ public class ExponentialBackoffTest extends ImsTestBase {
    }

    @After
    public void tearDown() {
    public void tearDown() throws Exception {
        mBackoffUnderTest.stop();
        super.tearDown();
    }

    @Test
Loading