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

Commit f64aa575 authored by Daniel Bright's avatar Daniel Bright Committed by Automerger Merge Worker
Browse files

Merge "Added global retry support" am: 1ceca0b1

Original change: https://android-review.googlesource.com/c/platform/frameworks/opt/telephony/+/1504162

Change-Id: I68e64ab0d3d972d09f8db2d7615dcbb769374c9d
parents 6332f9e0 1ceca0b1
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