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