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

Commit 0d6966cc authored by Jack Yu's avatar Jack Yu
Browse files

Fixed permananent failure behaviors

1. Non-conditional based evaluations will no longer trigger
   setup data if the APN was marked as permanent failure earlier.
   Only environmental changes can result in data setup.
2. Permanent failure can be only applied to the failed APN. When
   this happens, frameworks retries the next non-permanent-failed
   APN.

Fix: 254752857
Fix: 249908875
Test: Manual (verified on JP carrier) + atest FrameworksTelephonyTests
     + Basic testing.
Merged-In: Ic147c44b0729e8204cb494eb80932ef2c11f9861
Change-Id: Ic147c44b0729e8204cb494eb80932ef2c11f9861
parent 9b138dc2
Loading
Loading
Loading
Loading
+45 −21
Original line number Diff line number Diff line
@@ -195,50 +195,74 @@ public class DataEvaluation {
    @VisibleForTesting
    public enum DataEvaluationReason {
        /** New request from the apps. */
        NEW_REQUEST,
        NEW_REQUEST(false),
        /** Data config changed. */
        DATA_CONFIG_CHANGED,
        DATA_CONFIG_CHANGED(true),
        /** SIM is loaded. */
        SIM_LOADED,
        SIM_LOADED(true),
        /** SIM is removed. */
        SIM_REMOVAL,
        SIM_REMOVAL(true),
        /** Data profiles changed. */
        DATA_PROFILES_CHANGED,
        DATA_PROFILES_CHANGED(true),
        /** When service state changes.(For now only considering data RAT and data registration). */
        DATA_SERVICE_STATE_CHANGED,
        DATA_SERVICE_STATE_CHANGED(true),
        /** When data is enabled or disabled (by user, carrier, thermal, etc...) */
        DATA_ENABLED_CHANGED,
        DATA_ENABLED_CHANGED(true),
        /** When data enabled overrides are changed (MMS always allowed, data on non-DDS sub). */
        DATA_ENABLED_OVERRIDE_CHANGED,
        DATA_ENABLED_OVERRIDE_CHANGED(true),
        /** When data roaming is enabled or disabled. */
        ROAMING_ENABLED_CHANGED,
        ROAMING_ENABLED_CHANGED(true),
        /** When voice call ended (for concurrent voice/data not supported RAT). */
        VOICE_CALL_ENDED,
        VOICE_CALL_ENDED(true),
        /** When network restricts or no longer restricts mobile data. */
        DATA_RESTRICTED_CHANGED,
        DATA_RESTRICTED_CHANGED(true),
        /** Network capabilities changed. The unsatisfied requests might have chances to attach. */
        DATA_NETWORK_CAPABILITIES_CHANGED,
        DATA_NETWORK_CAPABILITIES_CHANGED(true),
        /** When emergency call started or ended. */
        EMERGENCY_CALL_CHANGED,
        EMERGENCY_CALL_CHANGED(true),
        /** When data disconnected, re-evaluate later to see if data could be brought up again. */
        RETRY_AFTER_DISCONNECTED,
        RETRY_AFTER_DISCONNECTED(true),
        /** Data setup retry. */
        DATA_RETRY,
        DATA_RETRY(false),
        /** For handover evaluation, or for network tearing down after handover succeeds/fails. */
        DATA_HANDOVER,
        DATA_HANDOVER(true),
        /** Preferred transport changed. */
        PREFERRED_TRANSPORT_CHANGED,
        PREFERRED_TRANSPORT_CHANGED(true),
        /** Slice config changed. */
        SLICE_CONFIG_CHANGED,
        SLICE_CONFIG_CHANGED(true),
        /**
         * Single data network arbitration. On certain RATs, only one data network is allowed at the
         * same time.
         */
        SINGLE_DATA_NETWORK_ARBITRATION,
        SINGLE_DATA_NETWORK_ARBITRATION(true),
        /** Query from {@link TelephonyManager#isDataConnectivityPossible()}. */
        EXTERNAL_QUERY,
        EXTERNAL_QUERY(false),
        /** Tracking area code changed. */
        TAC_CHANGED,
        TAC_CHANGED(true);

        /**
         * {@code true} if the evaluation is due to environmental changes (i.e. SIM removal,
         * registration state changes, etc....
         */
        private final boolean mIsConditionBased;

        /**
         * @return {@code true} if the evaluation is due to environmental changes (i.e. SIM removal,
         * registration state changes, etc....
         */
        public boolean isConditionBased() {
            return mIsConditionBased;
        }

        /**
         * Constructor
         *
         * @param isConditionBased {@code true} if the evaluation is due to environmental changes
         * (i.e. SIM removal, registration state changes, etc....)
         */
        DataEvaluationReason(boolean isConditionBased) {
            mIsConditionBased = isConditionBased;
        }
    }

    /** Disallowed reasons. There could be multiple reasons if it is not allowed. */
+1 −1
Original line number Diff line number Diff line
@@ -3131,7 +3131,7 @@ public class DataNetwork extends StateMachine {
                && !mAttachedNetworkRequestList.isEmpty()) {
            TelephonyNetworkRequest networkRequest = mAttachedNetworkRequestList.get(0);
            DataProfile dataProfile = mDataNetworkController.getDataProfileManager()
                    .getDataProfileForNetworkRequest(networkRequest, targetNetworkType);
                    .getDataProfileForNetworkRequest(networkRequest, targetNetworkType, true);
            // Some carriers have different profiles between cellular and IWLAN. We need to
            // dynamically switch profile, but only when those profiles have same APN name.
            if (dataProfile != null && dataProfile.getApnSetting() != null
+19 −2
Original line number Diff line number Diff line
@@ -551,6 +551,15 @@ public class DataNetworkController extends Handler {
         */
        public void onInternetDataNetworkConnected(@NonNull List<DataProfile> dataProfiles) {}

        /**
         * Called when data network is connected.
         *
         * @param transport Transport for the connected network.
         * @param dataProfile The data profile of the connected data network.
         */
        public void onDataNetworkConnected(@TransportType int transport,
                @NonNull DataProfile dataProfile) {}

        /** Called when internet data network is disconnected. */
        public void onInternetDataNetworkDisconnected() {}

@@ -1426,7 +1435,7 @@ public class DataNetworkController extends Handler {
        if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) {
            evaluation.addDataAllowedReason(DataAllowedReason.EMERGENCY_REQUEST);
            evaluation.setCandidateDataProfile(mDataProfileManager.getDataProfileForNetworkRequest(
                    networkRequest, getDataNetworkType(transport)));
                    networkRequest, getDataNetworkType(transport), true));
            networkRequest.setEvaluation(evaluation);
            log(evaluation.toString());
            return evaluation;
@@ -1575,7 +1584,10 @@ public class DataNetworkController extends Handler {
            networkType = mServiceState.getVoiceNetworkType();
        }
        DataProfile dataProfile = mDataProfileManager
                .getDataProfileForNetworkRequest(networkRequest, networkType);
                .getDataProfileForNetworkRequest(networkRequest, networkType,
                        // If the evaluation is due to environmental changes, then we should ignore
                        // the permanent failure reached earlier.
                        reason.isConditionBased());
        if (dataProfile == null) {
            evaluation.addDataDisallowedReason(DataDisallowedReason.NO_SUITABLE_DATA_PROFILE);
        } else if (reason == DataEvaluationReason.NEW_REQUEST
@@ -2604,6 +2616,11 @@ public class DataNetworkController extends Handler {
     */
    private void onDataNetworkConnected(@NonNull DataNetwork dataNetwork) {
        logl("onDataNetworkConnected: " + dataNetwork);

        mDataNetworkControllerCallbacks.forEach(callback -> callback.invokeFromExecutor(
                () -> callback.onDataNetworkConnected(dataNetwork.getTransport(),
                        dataNetwork.getDataProfile())));

        mPreviousConnectedDataNetworkList.add(0, dataNetwork);
        // Preserve the connected data networks for debugging purposes.
        if (mPreviousConnectedDataNetworkList.size() > MAX_HISTORICAL_CONNECTED_DATA_NETWORKS) {
+27 −4
Original line number Diff line number Diff line
@@ -621,14 +621,18 @@ public class DataProfileManager extends Handler {
     *
     * @param networkRequest The network request.
     * @param networkType The current data network type.
     * @param ignorePermanentFailure {@code true} to ignore {@link ApnSetting#getPermanentFailed()}.
     * This should be set to true for condition-based retry/setup.
     * @return The data profile. {@code null} if can't find any satisfiable data profile.
     */
    public @Nullable DataProfile getDataProfileForNetworkRequest(
            @NonNull TelephonyNetworkRequest networkRequest, @NetworkType int networkType) {
            @NonNull TelephonyNetworkRequest networkRequest, @NetworkType int networkType,
            boolean ignorePermanentFailure) {
        ApnSetting apnSetting = null;
        if (networkRequest.hasAttribute(TelephonyNetworkRequest
                .CAPABILITY_ATTRIBUTE_APN_SETTING)) {
            apnSetting = getApnSettingForNetworkRequest(networkRequest, networkType);
            apnSetting = getApnSettingForNetworkRequest(networkRequest, networkType,
                    ignorePermanentFailure);
        }

        TrafficDescriptor.Builder trafficDescriptorBuilder = new TrafficDescriptor.Builder();
@@ -687,10 +691,13 @@ public class DataProfileManager extends Handler {
     *
     * @param networkRequest The network request.
     * @param networkType The current data network type.
     * @param ignorePermanentFailure {@code true} to ignore {@link ApnSetting#getPermanentFailed()}.
     * This should be set to true for condition-based retry/setup.
     * @return The APN setting. {@code null} if can't find any satisfiable data profile.
     */
    private @Nullable ApnSetting getApnSettingForNetworkRequest(
            @NonNull TelephonyNetworkRequest networkRequest, @NetworkType int networkType) {
            @NonNull TelephonyNetworkRequest networkRequest, @NetworkType int networkType,
            boolean ignorePermanentFailure) {
        if (!networkRequest.hasAttribute(
                TelephonyNetworkRequest.CAPABILITY_ATTRIBUTE_APN_SETTING)) {
            loge("Network request does not have APN setting attribute.");
@@ -733,6 +740,7 @@ public class DataProfileManager extends Handler {
                        && (dp.getApnSetting().getApnSetId()
                        == Telephony.Carriers.MATCH_ALL_APN_SET_ID
                        || dp.getApnSetting().getApnSetId() == mPreferredDataProfileSetId))
                .filter(dp -> ignorePermanentFailure || !dp.getApnSetting().getPermanentFailed())
                .collect(Collectors.toList());
        if (dataProfiles.size() == 0) {
            log("Can't find any data profile has APN set id matched. mPreferredDataProfileSetId="
@@ -740,6 +748,15 @@ public class DataProfileManager extends Handler {
            return null;
        }

        // Check if data profiles are permanently failed.
        dataProfiles = dataProfiles.stream()
                .filter(dp -> ignorePermanentFailure || !dp.getApnSetting().getPermanentFailed())
                .collect(Collectors.toList());
        if (dataProfiles.size() == 0) {
            log("The suitable data profiles are all in permanent failed state.");
            return null;
        }

        return dataProfiles.get(0).getApnSetting();
    }

@@ -772,7 +789,7 @@ public class DataProfileManager extends Handler {
                new NetworkRequest.Builder()
                        .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
                        .build(), mPhone);
        return getDataProfileForNetworkRequest(networkRequest, networkType) != null;
        return getDataProfileForNetworkRequest(networkRequest, networkType, true) != null;
    }

     /**
@@ -1094,6 +1111,12 @@ public class DataProfileManager extends Handler {
        pw.println("Initial attach data profile=" + mInitialAttachDataProfile);
        pw.println("isTetheringDataProfileExisting=" + isTetheringDataProfileExisting(
                TelephonyManager.NETWORK_TYPE_LTE));
        pw.println("Permanent failed profiles=");
        pw.increaseIndent();
        mAllDataProfiles.stream()
                .filter(dp -> dp.getApnSetting() != null && dp.getApnSetting().getPermanentFailed())
                .forEach(pw::println);
        pw.decreaseIndent();

        pw.println("Local logs:");
        pw.increaseIndent();
+170 −88
Original line number Diff line number Diff line
@@ -56,7 +56,6 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
@@ -315,10 +314,6 @@ public class DataRetryManager extends Handler {
                }
            }

            if (mMaxRetries == 0) {
                mRetryIntervalsMillis = Collections.emptyList();
            }

            if (mMaxRetries < 0) {
                throw new IllegalArgumentException("Max retries should not be less than 0. "
                        + "mMaxRetries=" + mMaxRetries);
@@ -359,39 +354,45 @@ public class DataRetryManager extends Handler {
    }

    /**
     * Represent a setup data network retry rule.
     * Represent a rule for data setup retry.
     *
     * The syntax of the retry rule:
     * 1. Retry based on {@link NetworkCapabilities}. Note that only APN-type network
     * capabilities are supported.
     * "capabilities=[netCaps1|netCaps2|...], [retry_interval=n1|n2|n3|n4...],
     * [maximum_retries=n]"
     * 1. Retry based on {@link NetworkCapabilities}. Note that only APN-type network capabilities
     *    are supported. If the capabilities are not specified, then the retry rule only applies
     *    to the current failed APN used in setup data call request.
     * "capabilities=[netCaps1|netCaps2|...], [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]"
     *
     * 2. Retry based on {@link DataFailCause}
     * "fail_causes=[cause1|cause2|cause3|..], [retry_interval=n1|n2|n3|n4...],
     * [maximum_retries=n]"
     * "fail_causes=[cause1|cause2|cause3|..], [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]"
     *
     * 3. Retry based on {@link NetworkCapabilities} and {@link DataFailCause}. Note that only
     *    APN-type network capabilities are supported.
     * "capabilities=[netCaps1|netCaps2|...], fail_causes=[cause1|cause2|cause3|...],
     *     [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]"
     *
     * 4. Permanent fail causes (no timer-based retry) on the current failed APN. Retry interval
     *    is specified for retrying the next available APN.
     * "permanent_fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|65543|65547|
     *     2252|2253|2254, retry_interval=2500"
     *
     * For example,
     * "capabilities=eims, retry_interval=1000, maximum_retries=20" means if the attached
     * network request is emergency, then retry data network setup every 1 second for up to 20
     * times.
     *
     * "fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|2253|2254
     * , maximum_retries=0" means for those fail causes, never retry with timers. Note that
     * when environment changes, retry can still happen.
     *
     * "capabilities=internet|enterprise|dun|ims|fota, retry_interval=2500|3000|"
     * "5000|10000|15000|20000|40000|60000|120000|240000|600000|1200000|1800000"
     * "1800000, maximum_retries=20" means for those capabilities, retry happens in 2.5s, 3s,
     * 5s, 10s, 15s, 20s, 40s, 1m, 2m, 4m, 10m, 20m, 30m, 30m, 30m, until reaching 20 retries.
     * "1800000, maximum_retries=20" means for those capabilities, retry happens in 2.5s, 3s, 5s,
     * 10s, 15s, 20s, 40s, 1m, 2m, 4m, 10m, 20m, 30m, 30m, 30m, until reaching 20 retries.
     *
     */
    public static class DataSetupRetryRule extends DataRetryRule {
        private static final String RULE_TAG_PERMANENT_FAIL_CAUSES = "permanent_fail_causes";
        private static final String RULE_TAG_CAPABILITIES = "capabilities";

        /** {@code true} if this rule is for permanent fail causes. */
        private boolean mIsPermanentFailCauseRule;

        /**
         * Constructor
         *
@@ -410,8 +411,23 @@ public class DataRetryManager extends Handler {
                }
                String key = tokens[0].trim();
                String value = tokens[1].trim();
                if (key.equals(RULE_TAG_CAPABILITIES)) {
                    mNetworkCapabilities = DataUtils.getNetworkCapabilitiesFromString(value);
                try {
                    switch (key) {
                        case RULE_TAG_PERMANENT_FAIL_CAUSES:
                            mFailCauses = Arrays.stream(value.split("\\s*\\|\\s*"))
                                    .map(String::trim)
                                    .map(Integer::valueOf)
                                    .collect(Collectors.toSet());
                            mIsPermanentFailCauseRule = true;
                            break;
                        case RULE_TAG_CAPABILITIES:
                            mNetworkCapabilities = DataUtils
                                    .getNetworkCapabilitiesFromString(value);
                            break;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new IllegalArgumentException("illegal rule " + ruleString + ", e=" + e);
                }
            }

@@ -433,6 +449,13 @@ public class DataRetryManager extends Handler {
            return mNetworkCapabilities;
        }

        /**
         * @return {@code true} if this rule is for permanent fail causes.
         */
        public boolean isPermanentFailCauseRule() {
            return mIsPermanentFailCauseRule;
        }

        /**
         * Check if this rule can be matched.
         *
@@ -949,10 +972,27 @@ public class DataRetryManager extends Handler {
        });
        dataNetworkController.registerDataNetworkControllerCallback(
                new DataNetworkControllerCallback(this::post) {
                    /**
                     * Called when data service is bound.
                     *
                     * @param transport The transport of the data service.
                     */
                    @Override
                    public void onDataServiceBound(@TransportType int transport) {
                        onReset(RESET_REASON_DATA_SERVICE_BOUND);
                    }

                    /**
                     * Called when data network is connected.
                     *
                     * @param transport Transport for the connected network.
                     * @param dataProfile The data profile of the connected data network.
                     */
                    @Override
                    public void onDataNetworkConnected(@TransportType int transport,
                            @NonNull DataProfile dataProfile) {
                        DataRetryManager.this.onDataNetworkConnected(transport, dataProfile);
                    }
                });
        mRil.registerForOn(this, EVENT_RADIO_ON, null);
        mRil.registerForModemReset(this, EVENT_MODEM_RESET, null);
@@ -1001,7 +1041,7 @@ public class DataRetryManager extends Handler {
                } else if (ar.result instanceof DataProfile) {
                    dataProfile = (DataProfile) ar.result;
                }
                onDataProfileUnthrottled(dataProfile, apn, transport, true);
                onDataProfileUnthrottled(dataProfile, apn, transport, true, true);
                break;
            case EVENT_CANCEL_PENDING_HANDOVER_RETRY:
                onCancelPendingHandoverRetry((DataNetwork) msg.obj);
@@ -1034,6 +1074,21 @@ public class DataRetryManager extends Handler {
                + ", mDataHandoverRetryRuleList=" + mDataHandoverRetryRuleList);
    }

    /**
     * Called when data network is connected.
     *
     * @param transport Transport for the connected network.
     * @param dataProfile The data profile of the connected data network.
     */
    public void onDataNetworkConnected(@TransportType int transport,
            @NonNull DataProfile dataProfile) {
        if (dataProfile.getApnSetting() != null) {
            dataProfile.getApnSetting().setPermanentFailed(false);
        }

        onDataProfileUnthrottled(dataProfile, null, transport, true, false);
    }

    /**
     * Evaluate if data setup retry is needed or not. If needed, retry will be scheduled
     * automatically after evaluation.
@@ -1072,6 +1127,7 @@ public class DataRetryManager extends Handler {
            // ThrottleStatus is just for API backwards compatibility reason.
            updateThrottleStatus(dataProfile, requestList, null,
                    ThrottleStatus.RETRY_TYPE_NEW_CONNECTION, transport, Long.MAX_VALUE);
            return;
        } else if (retryDelayMillis != DataCallResponse.RETRY_DURATION_UNDEFINED) {
            // Network specifically asks retry the previous data profile again.
            DataSetupRetryEntry dataSetupRetryEntry = new DataSetupRetryEntry.Builder<>()
@@ -1085,23 +1141,46 @@ public class DataRetryManager extends Handler {
                    ThrottleStatus.RETRY_TYPE_NEW_CONNECTION, transport,
                    dataSetupRetryEntry.retryElapsedTime);
            schedule(dataSetupRetryEntry);
        } else {
            // Network did not suggest any retry. Use the configured rules to perform retry.
            logv("mDataSetupRetryRuleList=" + mDataSetupRetryRuleList);

            // Support the legacy permanent failure configuration
            if (DataFailCause.isPermanentFailure(mPhone.getContext(), cause, mPhone.getSubId())) {
                log("Stopped timer-based retry. cause=" + DataFailCause.toString(cause));
            return;
        }

        // Network did not suggest any retry. Use the configured rules to perform retry.
        logv("mDataSetupRetryRuleList=" + mDataSetupRetryRuleList);

        boolean retryScheduled = false;
        List<NetworkRequestList> groupedNetworkRequestLists =
                DataUtils.getGroupedNetworkRequestList(requestList);
        for (DataSetupRetryRule retryRule : mDataSetupRetryRuleList) {
            if (retryRule.isPermanentFailCauseRule() && retryRule.getFailCauses().contains(cause)) {
                if (dataProfile.getApnSetting() != null) {
                    dataProfile.getApnSetting().setPermanentFailed(true);

                    // It seems strange to have retry timer in permanent failure rule, but since
                    // in this case permanent failure is only applicable to the failed profile, so
                    // we need to see if other profile can be selected for next data setup.
                    log("Marked " + dataProfile.getApnSetting().getApnName() + " permanently "
                            + "failed, but still schedule retry to see if another data profile "
                            + "can be used for setup data.");
                    // Schedule a data retry to see if another data profile could be selected.
                    // If the same data profile is selected again, since it's marked as
                    // permanent failure, it won't be used for setup data call.
                    schedule(new DataSetupRetryEntry.Builder<>()
                            .setRetryDelay(retryRule.getRetryIntervalsMillis().get(0))
                            .setAppliedRetryRule(retryRule)
                            .setSetupRetryType(DataSetupRetryEntry.RETRY_TYPE_NETWORK_REQUESTS)
                            .setTransport(transport)
                            .setNetworkRequestList(requestList)
                            .build());
                } else {
                    // For TD-based data profile, do not do anything for now. Should expand this in
                    // the future if needed.
                    log("Stopped timer-based retry for TD-based data profile. Will retry only when "
                            + "environment changes.");
                }
                return;
            }
            for (NetworkRequestList networkRequestList : groupedNetworkRequestLists) {
                int capability = networkRequestList.get(0).getApnTypeNetworkCapability();

                for (DataSetupRetryRule retryRule : mDataSetupRetryRuleList) {
                if (retryRule.canBeMatched(capability, cause)) {
                    // Check if there is already a similar network request retry scheduled.
                    if (isSimilarNetworkRequestRetryScheduled(
@@ -1147,7 +1226,6 @@ public class DataRetryManager extends Handler {
                    + "retry.");
        }
    }
    }

    /**
     * Evaluate if data handover retry is needed or not. If needed, retry will be scheduled
@@ -1234,7 +1312,7 @@ public class DataRetryManager extends Handler {
            DataProfile dataProfile = dataThrottlingEntry.dataProfile;
            String apn = dataProfile.getApnSetting() != null
                    ? dataProfile.getApnSetting().getApnName() : null;
            onDataProfileUnthrottled(dataProfile, apn, dataThrottlingEntry.transport, false);
            onDataProfileUnthrottled(dataProfile, apn, dataThrottlingEntry.transport, false, true);
        }

        mDataThrottlingEntries.clear();
@@ -1393,9 +1471,10 @@ public class DataRetryManager extends Handler {
     * When this is set, {@code dataProfile} must be {@code null}.
     * @param transport The transport that this unthrottling request is on.
     * @param remove Whether to remove unthrottled entries from the list of entries.
     * @param retry Whether schedule data setup retry after unthrottling.
     */
    private void onDataProfileUnthrottled(@Nullable DataProfile dataProfile, @Nullable String apn,
            @TransportType int transport, boolean remove) {
            @TransportType int transport, boolean remove, boolean retry) {
        log("onDataProfileUnthrottled: data profile=" + dataProfile + ", apn=" + apn
                + ", transport=" + AccessNetworkConstants.transportTypeToString(transport)
                + ", remove=" + remove);
@@ -1412,6 +1491,7 @@ public class DataRetryManager extends Handler {
            Stream<DataThrottlingEntry> stream = mDataThrottlingEntries.stream();
            stream = stream.filter(entry -> entry.expirationTimeMillis > now);
            if (dataProfile.getApnSetting() != null) {
                dataProfile.getApnSetting().setPermanentFailed(false);
                stream = stream
                        .filter(entry -> entry.dataProfile.getApnSetting() != null)
                        .filter(entry -> entry.dataProfile.getApnSetting().getApnName()
@@ -1473,6 +1553,7 @@ public class DataRetryManager extends Handler {

        logl("onDataProfileUnthrottled: Removing the following throttling entries. "
                + dataUnthrottlingEntries);
        if (retry) {
            for (DataThrottlingEntry entry : dataUnthrottlingEntries) {
                // Immediately retry after unthrottling.
                if (entry.retryType == ThrottleStatus.RETRY_TYPE_NEW_CONNECTION) {
@@ -1490,6 +1571,7 @@ public class DataRetryManager extends Handler {
                            .build());
                }
            }
        }
        if (remove) {
            mDataThrottlingEntries.removeAll(dataUnthrottlingEntries);
        }
Loading