1 /** 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.telephony; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.compat.annotation.UnsupportedAppUsage; 22 import android.content.Context; 23 import android.os.Build; 24 import android.os.PersistableBundle; 25 import android.os.SystemClock; 26 import android.os.SystemProperties; 27 import android.telephony.Annotation.ApnType; 28 import android.telephony.CarrierConfigManager; 29 import android.telephony.data.ApnSetting; 30 import android.telephony.data.DataCallResponse; 31 import android.text.TextUtils; 32 import android.util.Pair; 33 34 import com.android.internal.telephony.dataconnection.DataThrottler; 35 import com.android.internal.telephony.util.TelephonyUtils; 36 import com.android.telephony.Rlog; 37 38 import java.util.ArrayList; 39 import java.util.Random; 40 41 /** 42 * Retry manager allows a simple way to declare a series of 43 * retry timeouts. After creating a RetryManager the configure 44 * method is used to define the sequence. A simple linear series 45 * may be initialized using configure with three integer parameters 46 * The other configure method allows a series to be declared using 47 * a string. 48 *<p> 49 * The format of the configuration string is the apn type followed by a series of parameters 50 * separated by a comma. There are two name value pair parameters plus a series 51 * of delay times. The units of of these delay times is unspecified. 52 * The name value pairs which may be specified are: 53 *<ul> 54 *<li>max_retries=<value> 55 *<li>default_randomizationTime=<value> 56 *</ul> 57 *<p> 58 * apn type specifies the APN type that the retry pattern will apply for. "others" is for all other 59 * APN types not specified in the config. 60 * 61 * max_retries is the number of times that incrementRetryCount 62 * maybe called before isRetryNeeded will return false. if value 63 * is infinite then isRetryNeeded will always return true. 64 * 65 * default_randomizationTime will be used as the randomizationTime 66 * for delay times which have no supplied randomizationTime. If 67 * default_randomizationTime is not defined it defaults to 0. 68 *<p> 69 * The other parameters define The series of delay times and each 70 * may have an optional randomization value separated from the 71 * delay time by a colon. 72 *<p> 73 * Examples: 74 * <ul> 75 * <li>3 retries for mms with no randomization value which means its 0: 76 * <ul><li><code>"mms:1000, 2000, 3000"</code></ul> 77 * 78 * <li>10 retries for default APN with a 500 default randomization value for each and 79 * the 4..10 retries all using 3000 as the delay: 80 * <ul><li><code>"default:max_retries=10, default_randomization=500, 1000, 2000, 3000"</code></ul> 81 * 82 * <li>4 retries for supl APN with a 100 as the default randomization value for the first 2 values 83 * and the other two having specified values of 500: 84 * <ul><li><code>"supl:default_randomization=100, 1000, 2000, 4000:500, 5000:500"</code></ul> 85 * 86 * <li>Infinite number of retries for all other APNs with the first one at 1000, the second at 2000 87 * all others will be at 3000. 88 * <ul><li><code>"others:max_retries=infinite,1000,2000,3000</code></ul> 89 * </ul> 90 * 91 * {@hide} 92 */ 93 public class RetryManager { 94 public static final String LOG_TAG = "RetryManager"; 95 public static final boolean DBG = true; 96 public static final boolean VDBG = false; // STOPSHIP if true 97 98 /** 99 * The default retry configuration for APNs. See above for the syntax. 100 */ 101 private static final String DEFAULT_DATA_RETRY_CONFIG = "max_retries=3, 5000, 5000, 5000"; 102 103 /** 104 * The APN type used for all other APNs retry configuration. 105 */ 106 private static final String OTHERS_APN_TYPE = "others"; 107 108 /** 109 * The default value (in milliseconds) for delay between APN trying (mInterApnDelay) 110 * within the same round 111 */ 112 private static final long DEFAULT_INTER_APN_DELAY = 20000; 113 114 /** 115 * The default value (in milliseconds) for delay between APN trying (mFailFastInterApnDelay) 116 * within the same round when we are in fail fast mode 117 */ 118 private static final long DEFAULT_INTER_APN_DELAY_FOR_PROVISIONING = 3000; 119 120 /** 121 * The default value (in milliseconds) for retrying APN after disconnect 122 */ 123 private static final long DEFAULT_APN_RETRY_AFTER_DISCONNECT_DELAY = 10000; 124 125 /** 126 * The value indicating retry should not occur. 127 */ 128 public static final long NO_RETRY = Long.MAX_VALUE; 129 130 /** 131 * The value indicating network did not suggest any retry delay 132 */ 133 public static final long NO_SUGGESTED_RETRY_DELAY = DataCallResponse.RETRY_DURATION_UNDEFINED; 134 135 /** 136 * If the network suggests a retry delay in the data call setup response, we will retry 137 * the current APN setting again. The maximum retry count is to prevent that network 138 * keeps asking device to retry data setup forever and causes power consumption issue. 139 */ 140 private static final int DEFAULT_MAX_SAME_APN_RETRY = 3; 141 142 /** 143 * The delay (in milliseconds) between APN trying within the same round 144 */ 145 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 146 private long mInterApnDelay; 147 148 /** 149 * The delay (in milliseconds) between APN trying within the same round when we are in 150 * fail fast mode 151 */ 152 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 153 private long mFailFastInterApnDelay; 154 155 /** 156 * The delay (in milliseconds) for APN retrying after disconnect (e.g. Modem suddenly reports 157 * data call lost) 158 */ 159 private long mApnRetryAfterDisconnectDelay; 160 161 /** 162 * The counter for same APN retrying. See {@link #DEFAULT_MAX_SAME_APN_RETRY} for the details. 163 */ 164 private int mSameApnRetryCount = 0; 165 166 /** 167 * The maximum times that frameworks retries data setup with the same APN. This value could be 168 * changed via carrier config. See {@link #DEFAULT_MAX_SAME_APN_RETRY} for the details. 169 */ 170 private int mMaxSameApnRetry = DEFAULT_MAX_SAME_APN_RETRY; 171 172 /** 173 * Retry record with times in milli-seconds 174 */ 175 private static class RetryRec { 176 long mDelayTime; 177 long mRandomizationTime; 178 RetryRec(long delayTime, long randomizationTime)179 RetryRec(long delayTime, long randomizationTime) { 180 mDelayTime = delayTime; 181 mRandomizationTime = randomizationTime; 182 } 183 } 184 185 /** 186 * The array of retry records 187 */ 188 private ArrayList<RetryRec> mRetryArray = new ArrayList<RetryRec>(); 189 190 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 191 private Phone mPhone; 192 193 private final DataThrottler mDataThrottler; 194 195 /** 196 * Flag indicating whether retrying forever regardless the maximum retry count mMaxRetryCount 197 */ 198 private boolean mRetryForever = false; 199 200 /** 201 * The maximum number of retries to attempt 202 */ 203 private int mMaxRetryCount; 204 205 /** 206 * The current number of retries 207 */ 208 private int mRetryCount = 0; 209 210 /** 211 * Random number generator. The random delay will be added into retry timer to avoid all devices 212 * around retrying the APN at the same time. 213 */ 214 private Random mRng = new Random(); 215 216 /** 217 * Retry manager configuration string. See top of the detailed explanation. 218 */ 219 private String mConfig; 220 221 /** 222 * The list to store APN setting candidates for data call setup. Most of the carriers only have 223 * one APN, but few carriers have more than one. 224 */ 225 private ArrayList<ApnSetting> mWaitingApns = new ArrayList<>(); 226 227 /** 228 * Index pointing to the current trying APN from mWaitingApns 229 */ 230 private int mCurrentApnIndex = -1; 231 232 /** 233 * Apn context type. 234 */ 235 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 236 private String mApnType; 237 238 private final @ApnType int apnType; 239 240 /** 241 * Retry manager constructor 242 * @param phone Phone object 243 * @param dataThrottler Data throttler 244 * @param apnType APN type 245 */ RetryManager(@onNull Phone phone, @NonNull DataThrottler dataThrottler, @ApnType int apnType)246 public RetryManager(@NonNull Phone phone, @NonNull DataThrottler dataThrottler, 247 @ApnType int apnType) { 248 mPhone = phone; 249 mDataThrottler = dataThrottler; 250 this.apnType = apnType; 251 } 252 253 /** 254 * Configure for using string which allow arbitrary 255 * sequences of times. See class comments for the 256 * string format. 257 * 258 * @return true if successful 259 */ 260 @UnsupportedAppUsage configure(String configStr)261 private boolean configure(String configStr) { 262 // Strip quotes if present. 263 if ((configStr.startsWith("\"") && configStr.endsWith("\""))) { 264 configStr = configStr.substring(1, configStr.length() - 1); 265 } 266 267 // Reset the retry manager since delay, max retry count, etc...will be reset. 268 reset(); 269 270 if (DBG) log("configure: '" + configStr + "'"); 271 mConfig = configStr; 272 273 if (!TextUtils.isEmpty(configStr)) { 274 long defaultRandomization = 0; 275 276 if (VDBG) log("configure: not empty"); 277 278 String strArray[] = configStr.split(","); 279 for (int i = 0; i < strArray.length; i++) { 280 if (VDBG) log("configure: strArray[" + i + "]='" + strArray[i] + "'"); 281 Pair<Boolean, Integer> value; 282 String splitStr[] = strArray[i].split("=", 2); 283 splitStr[0] = splitStr[0].trim(); 284 if (VDBG) log("configure: splitStr[0]='" + splitStr[0] + "'"); 285 if (splitStr.length > 1) { 286 splitStr[1] = splitStr[1].trim(); 287 if (VDBG) log("configure: splitStr[1]='" + splitStr[1] + "'"); 288 if (TextUtils.equals(splitStr[0], "default_randomization")) { 289 value = parseNonNegativeInt(splitStr[0], splitStr[1]); 290 if (!value.first) return false; 291 defaultRandomization = value.second; 292 } else if (TextUtils.equals(splitStr[0], "max_retries")) { 293 if (TextUtils.equals("infinite", splitStr[1])) { 294 mRetryForever = true; 295 } else { 296 value = parseNonNegativeInt(splitStr[0], splitStr[1]); 297 if (!value.first) return false; 298 mMaxRetryCount = value.second; 299 } 300 } else { 301 Rlog.e(LOG_TAG, "Unrecognized configuration name value pair: " 302 + strArray[i]); 303 return false; 304 } 305 } else { 306 /** 307 * Assume a retry time with an optional randomization value 308 * following a ":" 309 */ 310 splitStr = strArray[i].split(":", 2); 311 splitStr[0] = splitStr[0].trim(); 312 RetryRec rr = new RetryRec(0, 0); 313 value = parseNonNegativeInt("delayTime", splitStr[0]); 314 if (!value.first) return false; 315 rr.mDelayTime = value.second; 316 317 // Check if optional randomization value present 318 if (splitStr.length > 1) { 319 splitStr[1] = splitStr[1].trim(); 320 if (VDBG) log("configure: splitStr[1]='" + splitStr[1] + "'"); 321 value = parseNonNegativeInt("randomizationTime", splitStr[1]); 322 if (!value.first) return false; 323 rr.mRandomizationTime = value.second; 324 } else { 325 rr.mRandomizationTime = defaultRandomization; 326 } 327 mRetryArray.add(rr); 328 } 329 } 330 if (mRetryArray.size() > mMaxRetryCount) { 331 mMaxRetryCount = mRetryArray.size(); 332 if (VDBG) log("configure: setting mMaxRetryCount=" + mMaxRetryCount); 333 } 334 } else { 335 log("configure: cleared"); 336 } 337 338 if (VDBG) log("configure: true"); 339 return true; 340 } 341 342 /** 343 * Configure the retry manager 344 */ configureRetry()345 private void configureRetry() { 346 String configString = null; 347 String otherConfigString = null; 348 349 try { 350 if (TelephonyUtils.IS_DEBUGGABLE) { 351 // Using system properties is easier for testing from command line. 352 String config = SystemProperties.get("test.data_retry_config"); 353 if (!TextUtils.isEmpty(config)) { 354 configure(config); 355 return; 356 } 357 } 358 359 CarrierConfigManager configManager = (CarrierConfigManager) 360 mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); 361 PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId()); 362 363 mInterApnDelay = b.getLong( 364 CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG, 365 DEFAULT_INTER_APN_DELAY); 366 mFailFastInterApnDelay = b.getLong( 367 CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG, 368 DEFAULT_INTER_APN_DELAY_FOR_PROVISIONING); 369 mApnRetryAfterDisconnectDelay = b.getLong( 370 CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_RETRY_AFTER_DISCONNECT_LONG, 371 DEFAULT_APN_RETRY_AFTER_DISCONNECT_DELAY); 372 mMaxSameApnRetry = b.getInt( 373 CarrierConfigManager 374 .KEY_CARRIER_DATA_CALL_RETRY_NETWORK_REQUESTED_MAX_COUNT_INT, 375 DEFAULT_MAX_SAME_APN_RETRY); 376 377 // Load all retry patterns for all different APNs. 378 String[] allConfigStrings = b.getStringArray( 379 CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS); 380 if (allConfigStrings != null) { 381 for (String s : allConfigStrings) { 382 if (!TextUtils.isEmpty(s)) { 383 String splitStr[] = s.split(":", 2); 384 if (splitStr.length == 2) { 385 String apnTypeStr = splitStr[0].trim(); 386 // Check if this retry pattern is for the APN we want. 387 if (apnTypeStr.equals(ApnSetting.getApnTypeString(apnType))) { 388 // Extract the config string. Note that an empty string is valid 389 // here, meaning no retry for the specified APN. 390 configString = splitStr[1]; 391 break; 392 } else if (apnTypeStr.equals(OTHERS_APN_TYPE)) { 393 // Extract the config string. Note that an empty string is valid 394 // here, meaning no retry for all other APNs. 395 otherConfigString = splitStr[1]; 396 } 397 } 398 } 399 } 400 } 401 402 if (configString == null) { 403 if (otherConfigString != null) { 404 configString = otherConfigString; 405 } else { 406 // We should never reach here. If we reach here, it must be a configuration 407 // error bug. 408 log("Invalid APN retry configuration!. Use the default one now."); 409 configString = DEFAULT_DATA_RETRY_CONFIG; 410 } 411 } 412 } catch (NullPointerException ex) { 413 // We should never reach here unless there is a bug 414 log("Failed to read configuration! Use the hardcoded default value."); 415 416 mInterApnDelay = DEFAULT_INTER_APN_DELAY; 417 mFailFastInterApnDelay = DEFAULT_INTER_APN_DELAY_FOR_PROVISIONING; 418 configString = DEFAULT_DATA_RETRY_CONFIG; 419 } 420 421 if (VDBG) { 422 log("mInterApnDelay = " + mInterApnDelay + ", mFailFastInterApnDelay = " + 423 mFailFastInterApnDelay); 424 } 425 426 configure(configString); 427 } 428 429 /** 430 * Return the timer that should be used to trigger the data reconnection 431 */ 432 @UnsupportedAppUsage getRetryTimer()433 private long getRetryTimer() { 434 int index; 435 if (mRetryCount < mRetryArray.size()) { 436 index = mRetryCount; 437 } else { 438 index = mRetryArray.size() - 1; 439 } 440 441 long retVal; 442 if ((index >= 0) && (index < mRetryArray.size())) { 443 retVal = mRetryArray.get(index).mDelayTime + nextRandomizationTime(index); 444 } else { 445 retVal = 0; 446 } 447 448 if (DBG) log("getRetryTimer: " + retVal); 449 return retVal; 450 } 451 452 /** 453 * Parse an integer validating the value is not negative. 454 * @param name Name 455 * @param stringValue Value 456 * @return Pair.first == true if stringValue an integer >= 0 457 */ parseNonNegativeInt(String name, String stringValue)458 private Pair<Boolean, Integer> parseNonNegativeInt(String name, String stringValue) { 459 int value; 460 Pair<Boolean, Integer> retVal; 461 try { 462 value = Integer.parseInt(stringValue); 463 retVal = new Pair<>(validateNonNegativeInt(name, value), value); 464 } catch (NumberFormatException e) { 465 Rlog.e(LOG_TAG, name + " bad value: " + stringValue, e); 466 retVal = new Pair<>(false, 0); 467 } 468 if (VDBG) { 469 log("parseNonNegativeInt: " + name + ", " + stringValue + ", " 470 + retVal.first + ", " + retVal.second); 471 } 472 return retVal; 473 } 474 475 /** 476 * Validate an integer is >= 0 and logs an error if not 477 * @param name Name 478 * @param value Value 479 * @return Pair.first 480 */ validateNonNegativeInt(String name, long value)481 private boolean validateNonNegativeInt(String name, long value) { 482 boolean retVal; 483 if (value < 0) { 484 Rlog.e(LOG_TAG, name + " bad value: is < 0"); 485 retVal = false; 486 } else { 487 retVal = true; 488 } 489 if (VDBG) log("validateNonNegative: " + name + ", " + value + ", " + retVal); 490 return retVal; 491 } 492 493 /** 494 * Return next random number for the index 495 * @param index Retry index 496 */ nextRandomizationTime(int index)497 private long nextRandomizationTime(int index) { 498 long randomTime = mRetryArray.get(index).mRandomizationTime; 499 if (randomTime == 0) { 500 return 0; 501 } else { 502 return mRng.nextInt((int) randomTime); 503 } 504 } 505 getNetworkSuggestedRetryDelay()506 private long getNetworkSuggestedRetryDelay() { 507 long retryElapseTime = mDataThrottler.getRetryTime(apnType); 508 if (retryElapseTime == NO_RETRY || retryElapseTime == NO_SUGGESTED_RETRY_DELAY) { 509 return retryElapseTime; 510 } 511 512 // The time from data throttler is system's elapsed time. We need to return the delta. If 513 // less than 0, then return 0 (i.e. retry immediately). 514 return Math.max(0, retryElapseTime - SystemClock.elapsedRealtime()); 515 } 516 517 /** 518 * Get the next APN setting for data call setup. 519 * @return APN setting to try. {@code null} if cannot find any APN, 520 */ getNextApnSetting()521 public @Nullable ApnSetting getNextApnSetting() { 522 if (mWaitingApns == null || mWaitingApns.size() == 0) { 523 log("Waiting APN list is null or empty."); 524 return null; 525 } 526 527 long networkSuggestedRetryDelay = getNetworkSuggestedRetryDelay(); 528 if (networkSuggestedRetryDelay == NO_RETRY) { 529 log("Network suggested no retry."); 530 return null; 531 } 532 533 // If the network had suggested a retry delay, we should retry the current APN again 534 // (up to mMaxSameApnRetry times) instead of getting the next APN setting from 535 // our own list. If the APN waiting list has been reset before a setup data responses 536 // arrive (i.e. mCurrentApnIndex=-1), then ignore the network suggested retry. 537 if (mCurrentApnIndex != -1 && networkSuggestedRetryDelay != NO_SUGGESTED_RETRY_DELAY 538 && mSameApnRetryCount < mMaxSameApnRetry) { 539 mSameApnRetryCount++; 540 return mWaitingApns.get(mCurrentApnIndex); 541 } 542 543 mSameApnRetryCount = 0; 544 545 int index = mCurrentApnIndex; 546 // Loop through the APN list to find out the index of next non-permanent failed APN. 547 while (true) { 548 if (++index == mWaitingApns.size()) index = 0; 549 550 // Stop if we find the non-failed APN. 551 if (!mWaitingApns.get(index).getPermanentFailed()) { 552 break; 553 } 554 555 // If all APNs have permanently failed, bail out. 556 if (mWaitingApns.stream().allMatch(ApnSetting::getPermanentFailed)) { 557 return null; 558 } 559 } 560 561 mCurrentApnIndex = index; 562 return mWaitingApns.get(mCurrentApnIndex); 563 } 564 565 /** 566 * Get the delay for trying the next waiting APN from the list. 567 * @param failFastEnabled True if fail fast mode enabled. In this case we'll use a shorter 568 * delay. 569 * @return delay in milliseconds 570 */ getDelayForNextApn(boolean failFastEnabled)571 public long getDelayForNextApn(boolean failFastEnabled) { 572 573 if (mWaitingApns == null || mWaitingApns.size() == 0) { 574 log("Waiting APN list is null or empty."); 575 return NO_RETRY; 576 } 577 578 long networkSuggestedDelay = getNetworkSuggestedRetryDelay(); 579 log("Network suggested delay=" + networkSuggestedDelay + "ms"); 580 581 if (networkSuggestedDelay == NO_RETRY) { 582 log("Network suggested not retrying."); 583 return NO_RETRY; 584 } 585 586 if (networkSuggestedDelay != NO_SUGGESTED_RETRY_DELAY 587 && mSameApnRetryCount < mMaxSameApnRetry) { 588 // If the network explicitly suggests a retry delay, we should use it, even in fail fast 589 // mode. 590 log("Network suggested retry in " + networkSuggestedDelay + " ms."); 591 return networkSuggestedDelay; 592 } 593 594 // In order to determine the delay to try next APN, we need to peek the next available APN. 595 // Case 1 - If we will start the next round of APN trying, 596 // we use the exponential-growth delay. (e.g. 5s, 10s, 30s...etc.) 597 // Case 2 - If we are still within the same round of APN trying, 598 // we use the fixed standard delay between APNs. (e.g. 20s) 599 600 int index = mCurrentApnIndex; 601 while (true) { 602 if (++index >= mWaitingApns.size()) index = 0; 603 604 // Stop if we find the non-failed APN. 605 if (!mWaitingApns.get(index).getPermanentFailed()) { 606 break; 607 } 608 609 // If all APNs have permanently failed, bail out. 610 if (mWaitingApns.stream().allMatch(ApnSetting::getPermanentFailed)) { 611 log("All APNs have permanently failed."); 612 return NO_RETRY; 613 } 614 } 615 616 long delay; 617 if (index <= mCurrentApnIndex) { 618 // Case 1, if the next APN is in the next round. 619 if (!mRetryForever && mRetryCount + 1 > mMaxRetryCount) { 620 log("Reached maximum retry count " + mMaxRetryCount + "."); 621 return NO_RETRY; 622 } 623 delay = getRetryTimer(); 624 ++mRetryCount; 625 } else { 626 // Case 2, if the next APN is still in the same round. 627 delay = mInterApnDelay; 628 } 629 630 if (failFastEnabled && delay > mFailFastInterApnDelay) { 631 // If we enable fail fast mode, and the delay we got is longer than 632 // fail-fast delay (mFailFastInterApnDelay), use the fail-fast delay. 633 // If the delay we calculated is already shorter than fail-fast delay, 634 // then ignore fail-fast delay. 635 delay = mFailFastInterApnDelay; 636 } 637 638 return delay; 639 } 640 641 /** 642 * Mark the APN setting permanently failed. 643 * @param apn APN setting to be marked as permanently failed 644 * */ markApnPermanentFailed(ApnSetting apn)645 public void markApnPermanentFailed(ApnSetting apn) { 646 if (apn != null) { 647 apn.setPermanentFailed(true); 648 } 649 } 650 651 /** 652 * Reset the retry manager. 653 */ reset()654 private void reset() { 655 mMaxRetryCount = 0; 656 mRetryCount = 0; 657 mCurrentApnIndex = -1; 658 mSameApnRetryCount = 0; 659 mRetryArray.clear(); 660 } 661 662 /** 663 * Set waiting APNs for retrying in case needed. 664 * @param waitingApns Waiting APN list 665 */ setWaitingApns(ArrayList<ApnSetting> waitingApns)666 public void setWaitingApns(ArrayList<ApnSetting> waitingApns) { 667 668 if (waitingApns == null) { 669 log("No waiting APNs provided"); 670 return; 671 } 672 673 mWaitingApns = waitingApns; 674 675 // Since we replace the entire waiting APN list, we need to re-config this retry manager. 676 configureRetry(); 677 678 for (ApnSetting apn : mWaitingApns) { 679 apn.setPermanentFailed(false); 680 } 681 682 log("Setting " + mWaitingApns.size() + " waiting APNs."); 683 684 if (VDBG) { 685 for (int i = 0; i < mWaitingApns.size(); i++) { 686 log(" [" + i + "]:" + mWaitingApns.get(i)); 687 } 688 } 689 } 690 691 /** 692 * Get the list of waiting APNs. 693 * @return the list of waiting APNs 694 */ getWaitingApns()695 public @NonNull ArrayList<ApnSetting> getWaitingApns() { 696 return mWaitingApns; 697 } 698 699 /** 700 * Get the delay in milliseconds for APN retry after disconnect 701 * @return The delay in milliseconds 702 */ getRetryAfterDisconnectDelay()703 public long getRetryAfterDisconnectDelay() { 704 return mApnRetryAfterDisconnectDelay; 705 } 706 toString()707 public String toString() { 708 if (mConfig == null) return ""; 709 return "RetryManager: apnType=" + ApnSetting.getApnTypeString(apnType) 710 + " mRetryCount=" 711 + mRetryCount + " mMaxRetryCount=" + mMaxRetryCount + " mCurrentApnIndex=" 712 + mCurrentApnIndex + " mSameApnRtryCount=" + mSameApnRetryCount 713 + " networkSuggestedDelay=" + getNetworkSuggestedRetryDelay() + " mRetryForever=" 714 + mRetryForever + " mInterApnDelay=" + mInterApnDelay 715 + " mApnRetryAfterDisconnectDelay=" + mApnRetryAfterDisconnectDelay 716 + " mConfig={" + mConfig + "}"; 717 } 718 719 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) log(String s)720 private void log(String s) { 721 Rlog.d(LOG_TAG, "[" + ApnSetting.getApnTypeString(apnType) + "] " + s); 722 } 723 } 724