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