• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.io.FileDescriptor;
31 import java.io.PrintWriter;
32 import java.util.ArrayList;
33 import 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  */
84 public 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 {
RetryRec(int delayTime, int randomizationTime)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      */
RetryManager(Phone phone, String apnType)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      */
configure(String configStr)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      */
configureRetry(boolean forDefault)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      */
getRetryTimer()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      */
parseNonNegativeInt(String name, String stringValue)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      */
validateNonNegativeInt(String name, int value)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      */
nextRandomizationTime(int index)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      */
getNextApnSetting()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      */
getDelayForNextApn(boolean failFastEnabled)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      * */
markApnPermanentFailed(ApnSetting apn)557     public void markApnPermanentFailed(ApnSetting apn) {
558         if (apn != null) {
559             apn.permanentFailed = true;
560         }
561     }
562 
563     /**
564      * Reset the retry manager.
565      */
reset()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      */
setWaitingApns(ArrayList<ApnSetting> waitingApns)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      */
getWaitingApns()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      */
setModemSuggestedDelay(long delay)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      */
getInterApnDelay(boolean failFastEnabled)627     public long getInterApnDelay(boolean failFastEnabled) {
628         return (failFastEnabled) ? mFailFastInterApnDelay : mInterApnDelay;
629     }
630 
toString()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 
dump(FileDescriptor fd, PrintWriter pw, String[] args)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 
log(String s)662     private void log(String s) {
663         Rlog.d(LOG_TAG, "[" + mApnType + "] " + s);
664     }
665 }
666