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