Loading src/java/com/android/internal/telephony/RIL.java +16 −15 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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) Loading src/java/com/android/internal/telephony/RetryManager.java +75 −61 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. */ Loading @@ -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. */ Loading @@ -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; } /** Loading @@ -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 */ Loading Loading @@ -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; } /** Loading @@ -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"); Loading Loading @@ -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]; Loading Loading @@ -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; Loading @@ -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 { Loading @@ -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; Loading @@ -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"); Loading @@ -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()); } /** Loading @@ -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); Loading Loading @@ -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. Loading Loading @@ -620,7 +647,6 @@ public class RetryManager { mRetryCount = 0; mCurrentApnIndex = -1; mSameApnRetryCount = 0; mModemSuggestedDelay = NO_SUGGESTED_RETRY_DELAY; mRetryArray.clear(); } Loading Loading @@ -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 Loading @@ -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); } } src/java/com/android/internal/telephony/dataconnection/ApnContext.java +4 −13 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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)); } Loading Loading @@ -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 Loading src/java/com/android/internal/telephony/dataconnection/DataConnection.java +24 −13 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -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 Loading Loading @@ -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() { Loading src/java/com/android/internal/telephony/dataconnection/DataThrottler.java 0 → 100644 +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
src/java/com/android/internal/telephony/RIL.java +16 −15 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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) Loading
src/java/com/android/internal/telephony/RetryManager.java +75 −61 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. */ Loading @@ -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. */ Loading @@ -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; } /** Loading @@ -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 */ Loading Loading @@ -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; } /** Loading @@ -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"); Loading Loading @@ -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]; Loading Loading @@ -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; Loading @@ -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 { Loading @@ -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; Loading @@ -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"); Loading @@ -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()); } /** Loading @@ -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); Loading Loading @@ -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. Loading Loading @@ -620,7 +647,6 @@ public class RetryManager { mRetryCount = 0; mCurrentApnIndex = -1; mSameApnRetryCount = 0; mModemSuggestedDelay = NO_SUGGESTED_RETRY_DELAY; mRetryArray.clear(); } Loading Loading @@ -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 Loading @@ -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); } }
src/java/com/android/internal/telephony/dataconnection/ApnContext.java +4 −13 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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)); } Loading Loading @@ -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 Loading
src/java/com/android/internal/telephony/dataconnection/DataConnection.java +24 −13 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -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 Loading Loading @@ -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() { Loading
src/java/com/android/internal/telephony/dataconnection/DataThrottler.java 0 → 100644 +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; } }