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

Commit 1ceca0b1 authored by Daniel Bright's avatar Daniel Bright Committed by Gerrit Code Review
Browse files

Merge "Added global retry support"

parents 64e6a1ee a029d62c
Loading
Loading
Loading
Loading
+16 −15
Original line number Diff line number Diff line
@@ -7032,7 +7032,8 @@ public class RIL extends BaseCommands implements CommandsInterface {
    public static DataCallResponse convertDataCallResult(Object dcResult) {
        if (dcResult == null) return null;

        int cause, suggestedRetryTime, cid, active, mtu, mtuV4, mtuV6;
        int cause, cid, active, mtu, mtuV4, mtuV6;
        long suggestedRetryTime;
        String ifname;
        int protocolType;
        String[] addresses = null;
@@ -7113,22 +7114,22 @@ public class RIL extends BaseCommands implements CommandsInterface {
        } else if (dcResult instanceof android.hardware.radio.V1_6.SetupDataCallResult) {
            final android.hardware.radio.V1_6.SetupDataCallResult result =
                    (android.hardware.radio.V1_6.SetupDataCallResult) dcResult;
            cause = result.base.cause;
            suggestedRetryTime = result.base.suggestedRetryTime;
            cid = result.base.cid;
            active = result.base.active;
            protocolType = result.base.type;
            ifname = result.base.ifname;
            laList = result.base.addresses.stream().map(la -> createLinkAddressFromString(
            cause = result.cause;
            suggestedRetryTime = result.suggestedRetryTime;
            cid = result.cid;
            active = result.active;
            protocolType = result.type;
            ifname = result.ifname;
            laList = result.addresses.stream().map(la -> createLinkAddressFromString(
                    la.address, la.properties, la.deprecationTime, la.expirationTime))
                    .collect(Collectors.toList());

            dnses = result.base.dnses.stream().toArray(String[]::new);
            gateways = result.base.gateways.stream().toArray(String[]::new);
            pcscfs = result.base.pcscf.stream().toArray(String[]::new);
            mtu = Math.max(result.base.mtuV4, result.base.mtuV6);
            mtuV4 = result.base.mtuV4;
            mtuV6 = result.base.mtuV6;
            dnses = result.dnses.stream().toArray(String[]::new);
            gateways = result.gateways.stream().toArray(String[]::new);
            pcscfs = result.pcscf.stream().toArray(String[]::new);
            mtu = Math.max(result.mtuV4, result.mtuV6);
            mtuV4 = result.mtuV4;
            mtuV6 = result.mtuV6;
            handoverFailureMode = result.handoverFailureMode;
        } else {
            Rlog.e(RILJ_LOG_TAG, "Unsupported SetupDataCallResult " + dcResult);
@@ -7182,7 +7183,7 @@ public class RIL extends BaseCommands implements CommandsInterface {

        return new DataCallResponse.Builder()
                .setCause(cause)
                .setSuggestedRetryTime(suggestedRetryTime)
                .setRetryIntervalMillis(suggestedRetryTime)
                .setId(cid)
                .setLinkStatus(active)
                .setProtocolType(protocolType)
+75 −61
Original line number Diff line number Diff line
@@ -16,17 +16,22 @@

package com.android.internal.telephony;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Build;
import android.os.PersistableBundle;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.telephony.Annotation.ApnType;
import android.telephony.CarrierConfigManager;
import android.telephony.data.ApnSetting;
import android.telephony.data.DataCallResponse;
import android.text.TextUtils;
import android.util.Pair;

import com.android.internal.telephony.dataconnection.DataThrottler;
import com.android.internal.telephony.util.TelephonyUtils;
import com.android.telephony.Rlog;

@@ -118,18 +123,18 @@ public class RetryManager {
    private static final long DEFAULT_APN_RETRY_AFTER_DISCONNECT_DELAY = 10000;

    /**
     * The value indicating no retry is needed
     * The value indicating retry should not occur.
     */
    public static final long NO_RETRY = -1;
    public static final long NO_RETRY = Long.MAX_VALUE;

    /**
     * The value indicating modem did not suggest any retry delay
     * The value indicating network did not suggest any retry delay
     */
    public static final long NO_SUGGESTED_RETRY_DELAY = -2;
    public static final long NO_SUGGESTED_RETRY_DELAY = DataCallResponse.RETRY_INTERVAL_UNDEFINED;

    /**
     * If the modem suggests a retry delay in the data call setup response, we will retry
     * the current APN setting again. However, if the modem keeps suggesting retrying the same
     * If the network suggests a retry delay in the data call setup response, we will retry
     * the current APN setting again. However, if the network keeps suggesting retrying the same
     * APN setting, we'll fall into an infinite loop. Therefore adding a counter to retry up to
     * MAX_SAME_APN_RETRY times can avoid it.
     */
@@ -154,11 +159,6 @@ public class RetryManager {
     */
    private long mApnRetryAfterDisconnectDelay;

    /**
     * Modem suggested delay for retrying the current APN
     */
    private long mModemSuggestedDelay = NO_SUGGESTED_RETRY_DELAY;

    /**
     * The counter for same APN retrying. See MAX_SAME_APN_RETRY for the details.
     */
@@ -168,13 +168,13 @@ public class RetryManager {
     * Retry record with times in milli-seconds
     */
    private static class RetryRec {
        RetryRec(int delayTime, int randomizationTime) {
        long mDelayTime;
        long mRandomizationTime;

        RetryRec(long delayTime, long randomizationTime) {
            mDelayTime = delayTime;
            mRandomizationTime = randomizationTime;
        }

        int mDelayTime;
        int mRandomizationTime;
    }

    /**
@@ -185,6 +185,8 @@ public class RetryManager {
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    private Phone mPhone;

    private final DataThrottler mDataThrottler;

    /**
     * Flag indicating whether retrying forever regardless the maximum retry count mMaxRetryCount
     */
@@ -223,19 +225,24 @@ public class RetryManager {
    private int mCurrentApnIndex = -1;

    /**
     * Apn context type. Could be "default, "mms", "supl", etc...
     * Apn context type.
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    private String mApnType;

    private final @ApnType int apnType;

    /**
     * Retry manager constructor
     * @param phone Phone object
     * @param dataThrottler Data throttler
     * @param apnType APN type
     */
    public RetryManager(Phone phone, String apnType) {
    public RetryManager(@NonNull Phone phone, @NonNull DataThrottler dataThrottler,
            @ApnType int apnType) {
        mPhone = phone;
        mApnType = apnType;
        mDataThrottler = dataThrottler;
        this.apnType = apnType;
    }

    /**
@@ -259,7 +266,7 @@ public class RetryManager {
        mConfig = configStr;

        if (!TextUtils.isEmpty(configStr)) {
            int defaultRandomization = 0;
            long defaultRandomization = 0;

            if (VDBG) log("configure: not empty");

@@ -366,14 +373,14 @@ public class RetryManager {
                    if (!TextUtils.isEmpty(s)) {
                        String splitStr[] = s.split(":", 2);
                        if (splitStr.length == 2) {
                            String apnType = splitStr[0].trim();
                            String apnTypeStr = splitStr[0].trim();
                            // Check if this retry pattern is for the APN we want.
                            if (apnType.equals(mApnType)) {
                            if (apnTypeStr.equals(ApnSetting.getApnTypeString(apnType))) {
                                // Extract the config string. Note that an empty string is valid
                                // here, meaning no retry for the specified APN.
                                configString = splitStr[1];
                                break;
                            } else if (apnType.equals(OTHERS_APN_TYPE)) {
                            } else if (apnTypeStr.equals(OTHERS_APN_TYPE)) {
                                // Extract the config string. Note that an empty string is valid
                                // here, meaning no retry for all other APNs.
                                otherConfigString = splitStr[1];
@@ -414,7 +421,7 @@ public class RetryManager {
     * Return the timer that should be used to trigger the data reconnection
     */
    @UnsupportedAppUsage
    private int getRetryTimer() {
    private long getRetryTimer() {
        int index;
        if (mRetryCount < mRetryArray.size()) {
            index = mRetryCount;
@@ -422,7 +429,7 @@ public class RetryManager {
            index = mRetryArray.size() - 1;
        }

        int retVal;
        long retVal;
        if ((index >= 0) && (index < mRetryArray.size())) {
            retVal = mRetryArray.get(index).mDelayTime + nextRandomizationTime(index);
        } else {
@@ -444,13 +451,13 @@ public class RetryManager {
        Pair<Boolean, Integer> retVal;
        try {
            value = Integer.parseInt(stringValue);
            retVal = new Pair<Boolean, Integer>(validateNonNegativeInt(name, value), value);
            retVal = new Pair<>(validateNonNegativeInt(name, value), value);
        } catch (NumberFormatException e) {
            Rlog.e(LOG_TAG, name + " bad value: " + stringValue, e);
            retVal = new Pair<Boolean, Integer>(false, 0);
            retVal = new Pair<>(false, 0);
        }
        if (VDBG) {
            log("parseNonNetativeInt: " + name + ", " + stringValue + ", "
            log("parseNonNegativeInt: " + name + ", " + stringValue + ", "
                    + retVal.first + ", " + retVal.second);
        }
        return retVal;
@@ -462,7 +469,7 @@ public class RetryManager {
     * @param value Value
     * @return Pair.first
     */
    private boolean validateNonNegativeInt(String name, int value) {
    private boolean validateNonNegativeInt(String name, long value) {
        boolean retVal;
        if (value < 0) {
            Rlog.e(LOG_TAG, name + " bad value: is < 0");
@@ -478,13 +485,24 @@ public class RetryManager {
     * Return next random number for the index
     * @param index Retry index
     */
    private int nextRandomizationTime(int index) {
        int randomTime = mRetryArray.get(index).mRandomizationTime;
    private long nextRandomizationTime(int index) {
        long randomTime = mRetryArray.get(index).mRandomizationTime;
        if (randomTime == 0) {
            return 0;
        } else {
            return mRng.nextInt(randomTime);
            return mRng.nextInt((int) randomTime);
        }
    }

    private long getNetworkSuggestedRetryDelay() {
        long retryElapseTime = mDataThrottler.getRetryTime(apnType);
        if (retryElapseTime == NO_RETRY || retryElapseTime == NO_SUGGESTED_RETRY_DELAY) {
            return retryElapseTime;
        }

        // The time from data throttler is system's elapsed time. We need to return the delta. If
        // less than 0, then return 0 (i.e. retry immediately).
        return Math.max(0, retryElapseTime - SystemClock.elapsedRealtime());
    }

    /**
@@ -497,11 +515,17 @@ public class RetryManager {
            return null;
        }

        // If the modem had suggested a retry delay, we should retry the current APN again
        long networkSuggestedRetryDelay = getNetworkSuggestedRetryDelay();
        if (networkSuggestedRetryDelay == NO_RETRY) {
            log("Network suggested no retry.");
            return null;
        }

        // If the network had suggested a retry delay, we should retry the current APN again
        // (up to MAX_SAME_APN_RETRY times) instead of getting the next APN setting from
        // our own list. If the APN waiting list has been reset before a setup data responses
        // arrive (i.e. mCurrentApnIndex=-1), then ignore the modem suggested retry.
        if (mCurrentApnIndex != -1 && mModemSuggestedDelay != NO_SUGGESTED_RETRY_DELAY
        // arrive (i.e. mCurrentApnIndex=-1), then ignore the network suggested retry.
        if (mCurrentApnIndex != -1 && networkSuggestedRetryDelay != NO_SUGGESTED_RETRY_DELAY
                && mSameApnRetryCount < MAX_SAME_APN_RETRY) {
            mSameApnRetryCount++;
            return mWaitingApns.get(mCurrentApnIndex);
@@ -542,17 +566,20 @@ public class RetryManager {
            return NO_RETRY;
        }

        if (mModemSuggestedDelay == NO_RETRY) {
            log("Modem suggested not retrying.");
        long networkSuggestedDelay = getNetworkSuggestedRetryDelay();
        log("Network suggested delay=" + networkSuggestedDelay + "ms");

        if (networkSuggestedDelay == NO_RETRY) {
            log("Network suggested not retrying.");
            return NO_RETRY;
        }

        if (mModemSuggestedDelay != NO_SUGGESTED_RETRY_DELAY &&
                mSameApnRetryCount < MAX_SAME_APN_RETRY) {
            // If the modem explicitly suggests a retry delay, we should use it, even in fail fast
        if (networkSuggestedDelay != NO_SUGGESTED_RETRY_DELAY
                && mSameApnRetryCount < MAX_SAME_APN_RETRY) {
            // If the network explicitly suggests a retry delay, we should use it, even in fail fast
            // mode.
            log("Modem suggested retry in " + mModemSuggestedDelay + " ms.");
            return mModemSuggestedDelay;
            log("Network suggested retry in " + networkSuggestedDelay + " ms.");
            return networkSuggestedDelay;
        }

        // In order to determine the delay to try next APN, we need to peek the next available APN.
@@ -620,7 +647,6 @@ public class RetryManager {
        mRetryCount = 0;
        mCurrentApnIndex = -1;
        mSameApnRetryCount = 0;
        mModemSuggestedDelay = NO_SUGGESTED_RETRY_DELAY;
        mRetryArray.clear();
    }

@@ -661,19 +687,6 @@ public class RetryManager {
        return mWaitingApns;
    }

    /**
     * Save the modem suggested delay for retrying the current APN.
     * This method is called when we get the suggested delay from RIL.
     * @param delay The delay in milliseconds
     */
    public void setModemSuggestedDelay(long delay) {
        if (mCurrentApnIndex == -1) {
            log("Waiting APN list has been reset. Ignore the value from modem.");
            return;
        }
        mModemSuggestedDelay = delay;
    }

    /**
     * Get the delay in milliseconds for APN retry after disconnect
     * @return The delay in milliseconds
@@ -684,16 +697,17 @@ public class RetryManager {

    public String toString() {
        if (mConfig == null) return "";
        return "RetryManager: mApnType=" + mApnType + " mRetryCount=" + mRetryCount
                + " mMaxRetryCount=" + mMaxRetryCount + " mCurrentApnIndex=" + mCurrentApnIndex
                + " mSameApnRtryCount=" + mSameApnRetryCount + " mModemSuggestedDelay="
                + mModemSuggestedDelay + " mRetryForever=" + mRetryForever + " mInterApnDelay="
                + mInterApnDelay + " mApnRetryAfterDisconnectDelay=" + mApnRetryAfterDisconnectDelay
        return "RetryManager: apnType=" + ApnSetting.getApnTypeString(apnType) + " mRetryCount="
                + mRetryCount + " mMaxRetryCount=" + mMaxRetryCount + " mCurrentApnIndex="
                + mCurrentApnIndex + " mSameApnRtryCount=" + mSameApnRetryCount
                + " networkSuggestedDelay=" + getNetworkSuggestedRetryDelay() + " mRetryForever="
                + mRetryForever + " mInterApnDelay=" + mInterApnDelay
                + " mApnRetryAfterDisconnectDelay=" + mApnRetryAfterDisconnectDelay
                + " mConfig={" + mConfig + "}";
    }

    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    private void log(String s) {
        Rlog.d(LOG_TAG, "[" + mApnType + "] " + s);
        Rlog.d(LOG_TAG, "[" + ApnSetting.getApnTypeString(apnType) + "] " + s);
    }
}
+4 −13
Original line number Diff line number Diff line
@@ -113,8 +113,7 @@ public class ApnContext {
     * @param tracker Data call tracker
     * @param priority Priority of APN type
     */
    public ApnContext(Phone phone, String apnType, String logTag, DcTracker tracker,
            int priority) {
    public ApnContext(Phone phone, String apnType, String logTag, DcTracker tracker, int priority) {
        mPhone = phone;
        mApnType = apnType;
        mState = DctConstants.State.IDLE;
@@ -123,7 +122,8 @@ public class ApnContext {
        mPriority = priority;
        LOG_TAG = logTag;
        mDcTracker = tracker;
        mRetryManager = new RetryManager(phone, apnType);
        mRetryManager = new RetryManager(phone, tracker.getDataThrottler(),
                ApnSetting.getApnTypesBitmaskFromString(apnType));
    }


@@ -233,15 +233,6 @@ public class ApnContext {
        return mRetryManager.getNextApnSetting();
    }

    /**
     * Save the modem suggested delay for retrying the current APN.
     * This method is called when we get the suggested delay from RIL.
     * @param delay The delay in milliseconds
     */
    public void setModemSuggestedDelay(long delay) {
        mRetryManager.setModemSuggestedDelay(delay);
    }

    /**
     * Get the delay for trying the next APN setting if the current one failed.
     * @param failFastEnabled True if fail fast mode enabled. In this case we'll use a shorter
+24 −13
Original line number Diff line number Diff line
@@ -715,7 +715,7 @@ public class DataConnection extends StateMachine {
        if (mDcTesterFailBringUpAll.getDcFailBringUp().mCounter  > 0) {
            DataCallResponse response = new DataCallResponse.Builder()
                    .setCause(mDcTesterFailBringUpAll.getDcFailBringUp().mFailCause)
                    .setSuggestedRetryTime(
                    .setRetryIntervalMillis(
                            mDcTesterFailBringUpAll.getDcFailBringUp().mSuggestedRetryTime)
                    .setMtuV4(PhoneConstants.UNSET_MTU)
                    .setMtuV6(PhoneConstants.UNSET_MTU)
@@ -2106,7 +2106,14 @@ public class DataConnection extends StateMachine {
                            // NO_SUGGESTED_RETRY_DELAY here.

                            long delay = getSuggestedRetryDelay(dataCallResponse);
                            cp.mApnContext.setModemSuggestedDelay(delay);
                            long retryTime = RetryManager.NO_SUGGESTED_RETRY_DELAY;
                            if (delay == RetryManager.NO_RETRY) {
                                retryTime = RetryManager.NO_RETRY;
                            } else if (delay >= 0) {
                                retryTime = SystemClock.elapsedRealtime() + delay;
                            }
                            mDct.getDataThrottler().setRetryTime(mApnSetting.getApnTypeBitmask(),
                                    retryTime);

                            String str = "DcActivatingState: ERROR_DATA_SERVICE_SPECIFIC_ERROR "
                                    + " delay=" + delay
@@ -2903,30 +2910,34 @@ public class DataConnection extends StateMachine {
     * Using the result of the SETUP_DATA_CALL determine the retry delay.
     *
     * @param response The response from setup data call
     * @return NO_SUGGESTED_RETRY_DELAY if no retry is needed otherwise the delay to the
     *         next SETUP_DATA_CALL
     * @return {@link RetryManager#NO_SUGGESTED_RETRY_DELAY} if not suggested.
     * {@link RetryManager#NO_RETRY} if retry should not happen. Otherwise the delay in milliseconds
     * to the next SETUP_DATA_CALL.
     */
    private long getSuggestedRetryDelay(DataCallResponse response) {
        /** According to ril.h
         * The value < 0 means no value is suggested
         * The value 0 means retry should be done ASAP.
         * The value of Integer.MAX_VALUE(0x7fffffff) means no retry.
         * The value of Long.MAX_VALUE(0x7fffffffffffffff) means no retry.
         */

        long suggestedRetryTime = response.getRetryIntervalMillis();

        // The value < 0 means no value is suggested
        if (response.getSuggestedRetryTime() < 0) {
        if (suggestedRetryTime < 0) {
            if (DBG) log("No suggested retry delay.");
            return RetryManager.NO_SUGGESTED_RETRY_DELAY;
        }
        // The value of Integer.MAX_VALUE(0x7fffffff) means no retry.
        else if (response.getSuggestedRetryTime() == Integer.MAX_VALUE) {
            if (DBG) log("Modem suggested not retrying.");
        } else if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_6)
                && suggestedRetryTime == Long.MAX_VALUE) {
            if (DBG) log("Network suggested not retrying.");
            return RetryManager.NO_RETRY;
        } else if (mPhone.getHalVersion().less(RIL.RADIO_HAL_VERSION_1_6)
                && suggestedRetryTime == Integer.MAX_VALUE) {
            if (DBG) log("Network suggested not retrying.");
            return RetryManager.NO_RETRY;
        }

        // We need to cast it to long because the value returned from RIL is a 32-bit integer,
        // but the time values used in AlarmManager are all 64-bit long.
        return (long) response.getSuggestedRetryTime();
        return suggestedRetryTime;
    }

    public List<ApnContext> getApnContexts() {
+80 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.telephony.dataconnection;

import android.telephony.Annotation.ApnType;
import android.telephony.data.ApnSetting;

import com.android.internal.telephony.RetryManager;

import java.util.HashMap;
import java.util.Map;

/**
 * Data throttler tracks the throttling status of data network. The throttler is per phone and per
 * transport type.
 */
public class DataThrottler {
    /**
     * Throttling tracker for APNs. Key is the APN type. Value is the elapsed time that APN
     * should not be retried. {@link RetryManager#NO_SUGGESTED_RETRY_DELAY} indicates throttling
     * does not exist. {@link RetryManager#NO_RETRY} indicates retry should never happen.
     */
    private final Map<Integer, Long> mThrottlingTracker = new HashMap<>();

    /**
     * Set retry time for the APN type.
     *
     * @param apnTypes APN types
     * @param retryElapsedTime The elapsed time that data connection for APN types should not be
     * retried. {@link RetryManager#NO_SUGGESTED_RETRY_DELAY} indicates throttling does not exist.
     * {@link RetryManager#NO_RETRY} indicates retry should never happen.
     */
    public void setRetryTime(@ApnType int apnTypes, long retryElapsedTime) {
        if (retryElapsedTime < 0) {
            retryElapsedTime = RetryManager.NO_SUGGESTED_RETRY_DELAY;
        }
        while (apnTypes != 0) {
            // Extract the least significant bit.
            int apnType = apnTypes & -apnTypes;
            mThrottlingTracker.put(apnType, retryElapsedTime);
            // Remove the least significant bit.
            apnTypes &= apnTypes - 1;
        }
    }

    /**
     * Get the earliest retry time for given APN type. The time is the system's elapse time.
     *
     * @param apnType APN type
     * @return The earliest retry time for APN type. The time is the system's elapse time.
     * {@link RetryManager#NO_SUGGESTED_RETRY_DELAY} indicates there is no throttling for given APN
     * type, {@link RetryManager#NO_RETRY} indicates retry should never happen.
     */
    public long getRetryTime(@ApnType int apnType) {
        // This is the workaround to handle the mistake that
        // ApnSetting.TYPE_DEFAULT = ApnTypes.DEFAULT | ApnTypes.HIPRI.
        if (apnType == ApnSetting.TYPE_DEFAULT) {
            apnType &= ~(ApnSetting.TYPE_HIPRI);
        }
        if (mThrottlingTracker.containsKey(apnType)) {
            return mThrottlingTracker.get(apnType);
        }

        return RetryManager.NO_SUGGESTED_RETRY_DELAY;
    }
}
Loading