1 /* 2 * Copyright 2021 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.data; 18 19 import android.annotation.CallbackExecutor; 20 import android.annotation.ElapsedRealtimeLong; 21 import android.annotation.IntDef; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.app.AlarmManager; 25 import android.app.PendingIntent; 26 import android.content.BroadcastReceiver; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.IntentFilter; 30 import android.net.NetworkCapabilities; 31 import android.os.AsyncResult; 32 import android.os.Handler; 33 import android.os.Looper; 34 import android.os.Message; 35 import android.os.SystemClock; 36 import android.telephony.AccessNetworkConstants; 37 import android.telephony.AccessNetworkConstants.TransportType; 38 import android.telephony.Annotation.DataFailureCause; 39 import android.telephony.Annotation.NetCapability; 40 import android.telephony.AnomalyReporter; 41 import android.telephony.DataFailCause; 42 import android.telephony.data.DataCallResponse; 43 import android.telephony.data.DataProfile; 44 import android.telephony.data.ThrottleStatus; 45 import android.telephony.data.ThrottleStatus.RetryType; 46 import android.text.TextUtils; 47 import android.util.ArraySet; 48 import android.util.IndentingPrintWriter; 49 import android.util.LocalLog; 50 import android.util.SparseArray; 51 52 import com.android.internal.annotations.VisibleForTesting; 53 import com.android.internal.telephony.CommandsInterface; 54 import com.android.internal.telephony.Phone; 55 import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback; 56 import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback; 57 import com.android.internal.telephony.data.DataNetworkController.NetworkRequestList; 58 import com.android.internal.telephony.data.DataProfileManager.DataProfileManagerCallback; 59 import com.android.internal.telephony.flags.FeatureFlags; 60 import com.android.telephony.Rlog; 61 62 import java.io.FileDescriptor; 63 import java.io.PrintWriter; 64 import java.util.ArrayList; 65 import java.util.Arrays; 66 import java.util.List; 67 import java.util.Locale; 68 import java.util.Objects; 69 import java.util.Set; 70 import java.util.UUID; 71 import java.util.concurrent.Executor; 72 import java.util.concurrent.TimeUnit; 73 import java.util.stream.Collectors; 74 import java.util.stream.Stream; 75 76 /** 77 * DataRetryManager manages data network setup retry and its configurations. 78 */ 79 public class DataRetryManager extends Handler { 80 private static final boolean VDBG = false; 81 82 /** Intent of Alarm Manager for long retry timer. */ 83 private static final String ACTION_RETRY = "com.android.internal.telephony.data.ACTION_RETRY"; 84 /** The extra key for the hashcode of the retry entry for Alarm Manager. */ 85 private static final String ACTION_RETRY_EXTRA_HASHCODE = "extra_retry_hashcode"; 86 87 /** Event for data setup retry. */ 88 private static final int EVENT_DATA_SETUP_RETRY = 3; 89 90 /** Event for data handover retry. */ 91 private static final int EVENT_DATA_HANDOVER_RETRY = 4; 92 93 /** Event for data profile/apn unthrottled. */ 94 private static final int EVENT_DATA_PROFILE_UNTHROTTLED = 6; 95 96 /** Event for cancelling pending handover retry. */ 97 private static final int EVENT_CANCEL_PENDING_HANDOVER_RETRY = 7; 98 99 /** 100 * Event for radio on. This can happen when airplane mode is turned off, or RIL crashes and came 101 * back online. 102 */ 103 private static final int EVENT_RADIO_ON = 8; 104 105 /** Event for modem reset. */ 106 private static final int EVENT_MODEM_RESET = 9; 107 108 /** Event for tracking area code change. */ 109 private static final int EVENT_TAC_CHANGED = 10; 110 111 /** The maximum entries to preserve. */ 112 private static final int MAXIMUM_HISTORICAL_ENTRIES = 100; 113 /** 114 * The threshold of retry timer, longer than or equal to which we use alarm manager to schedule 115 * instead of handler. 116 */ 117 private static final long RETRY_LONG_DELAY_TIMER_THRESHOLD_MILLIS = TimeUnit 118 .MINUTES.toMillis(1); 119 120 @IntDef(prefix = {"RESET_REASON_"}, 121 value = { 122 RESET_REASON_DATA_PROFILES_CHANGED, 123 RESET_REASON_RADIO_ON, 124 RESET_REASON_MODEM_RESTART, 125 RESET_REASON_DATA_SERVICE_BOUND, 126 RESET_REASON_DATA_CONFIG_CHANGED, 127 RESET_REASON_TAC_CHANGED, 128 }) 129 public @interface RetryResetReason {} 130 131 /** Reset due to data profiles changed. */ 132 private static final int RESET_REASON_DATA_PROFILES_CHANGED = 1; 133 134 /** Reset due to radio on. This could happen after airplane mode off or RIL restarted. */ 135 private static final int RESET_REASON_RADIO_ON = 2; 136 137 /** Reset due to modem restarted. */ 138 private static final int RESET_REASON_MODEM_RESTART = 3; 139 140 /** 141 * Reset due to data service bound. This could happen when reboot or when data service crashed 142 * and rebound. 143 */ 144 private static final int RESET_REASON_DATA_SERVICE_BOUND = 4; 145 146 /** Reset due to data config changed. */ 147 private static final int RESET_REASON_DATA_CONFIG_CHANGED = 5; 148 149 /** Reset due to tracking area code changed. */ 150 private static final int RESET_REASON_TAC_CHANGED = 6; 151 152 /** The phone instance. */ 153 @NonNull 154 private final Phone mPhone; 155 156 /** Featureflags. */ 157 @NonNull 158 private final FeatureFlags mFlags; 159 160 /** The RIL instance. */ 161 @NonNull 162 private final CommandsInterface mRil; 163 164 /** Logging tag. */ 165 @NonNull 166 private final String mLogTag; 167 168 /** Local log. */ 169 @NonNull 170 private final LocalLog mLocalLog = new LocalLog(128); 171 172 /** Alarm Manager used to schedule long set up or handover retries. */ 173 @NonNull 174 private final AlarmManager mAlarmManager; 175 176 /** 177 * The data retry callback. This is only used to notify {@link DataNetworkController} to retry 178 * setup data network. 179 */ 180 @NonNull 181 private final Set<DataRetryManagerCallback> mDataRetryManagerCallbacks = new ArraySet<>(); 182 183 /** Data service managers. */ 184 @NonNull 185 private final SparseArray<DataServiceManager> mDataServiceManagers; 186 187 /** Data config manager instance. */ 188 @NonNull 189 private final DataConfigManager mDataConfigManager; 190 191 /** Data profile manager. */ 192 @NonNull 193 private final DataProfileManager mDataProfileManager; 194 195 /** Data setup retry rule list. */ 196 @NonNull 197 private List<DataSetupRetryRule> mDataSetupRetryRuleList = new ArrayList<>(); 198 199 /** Data handover retry rule list. */ 200 @NonNull 201 private List<DataHandoverRetryRule> mDataHandoverRetryRuleList = new ArrayList<>(); 202 203 /** Data retry entries. */ 204 @NonNull 205 private final List<DataRetryEntry> mDataRetryEntries = new ArrayList<>(); 206 207 /** 208 * Data throttling entries. Note this only stores throttling requested by networks. We intended 209 * not to store frameworks-initiated throttling because they are not explicit/strong throttling 210 * requests. 211 */ 212 @NonNull 213 private final List<DataThrottlingEntry> mDataThrottlingEntries = new ArrayList<>(); 214 215 /** 216 * Represent a single data setup/handover throttling reported by networks. 217 */ 218 public static class DataThrottlingEntry { 219 /** 220 * The data profile that is being throttled for setup/handover retry. 221 */ 222 @NonNull 223 public final DataProfile dataProfile; 224 225 /** 226 * The associated network request list when throttling happened. Should be {@code null} when 227 * retry type is {@link ThrottleStatus#RETRY_TYPE_HANDOVER}. 228 */ 229 @Nullable 230 public final NetworkRequestList networkRequestList; 231 232 /** 233 * The data network that is being throttled for handover retry. Should be 234 * {@code null} when retryType is {@link ThrottleStatus#RETRY_TYPE_NEW_CONNECTION}. 235 */ 236 @Nullable 237 public final DataNetwork dataNetwork; 238 239 /** The transport that the data profile has been throttled on. */ 240 @TransportType 241 public final int transport; 242 243 /** The retry type when throttling expires. */ 244 @RetryType 245 public final int retryType; 246 247 /** 248 * The expiration time of data throttling. This is the time retrieved from 249 * {@link SystemClock#elapsedRealtime()}. 250 */ 251 @ElapsedRealtimeLong 252 public final long expirationTimeMillis; 253 254 /** 255 * Constructor. 256 * 257 * @param dataProfile The data profile that is being throttled for setup/handover retry. 258 * @param networkRequestList The associated network request list when throttling happened. 259 * Should be {@code null} when retry type is {@link ThrottleStatus#RETRY_TYPE_HANDOVER}. 260 * @param dataNetwork The data network that is being throttled for handover retry. 261 * Should be {@code null} when retryType is 262 * {@link ThrottleStatus#RETRY_TYPE_NEW_CONNECTION}. 263 * @param transport The transport that the data profile has been throttled on. 264 * @param retryType The retry type when throttling expires. 265 * @param expirationTimeMillis The expiration elapsed time of data throttling. 266 */ DataThrottlingEntry(@onNull DataProfile dataProfile, @Nullable NetworkRequestList networkRequestList, @Nullable DataNetwork dataNetwork, @TransportType int transport, @RetryType int retryType, @ElapsedRealtimeLong long expirationTimeMillis)267 public DataThrottlingEntry(@NonNull DataProfile dataProfile, 268 @Nullable NetworkRequestList networkRequestList, 269 @Nullable DataNetwork dataNetwork, @TransportType int transport, 270 @RetryType int retryType, @ElapsedRealtimeLong long expirationTimeMillis) { 271 this.dataProfile = dataProfile; 272 this.networkRequestList = networkRequestList; 273 this.dataNetwork = dataNetwork; 274 this.transport = transport; 275 this.retryType = retryType; 276 this.expirationTimeMillis = expirationTimeMillis; 277 } 278 279 @Override 280 @NonNull toString()281 public String toString() { 282 return "[DataThrottlingEntry: dataProfile=" + dataProfile + ", request list=" 283 + networkRequestList + ", dataNetwork=" + dataNetwork + ", transport=" 284 + AccessNetworkConstants.transportTypeToString(transport) + ", expiration time=" 285 + DataUtils.elapsedTimeToString(expirationTimeMillis) + "]"; 286 } 287 } 288 289 /** 290 * Represent a data retry rule. A rule consists a retry type (e.g. either by capabilities, 291 * fail cause, or both), and a retry interval. 292 */ 293 public static class DataRetryRule { 294 private static final String RULE_TAG_FAIL_CAUSES = "fail_causes"; 295 private static final String RULE_TAG_RETRY_INTERVAL = "retry_interval"; 296 private static final String RULE_TAG_MAXIMUM_RETRIES = "maximum_retries"; 297 298 /** 299 * The data network setup retry interval. Note that if this is empty, then 300 * {@link #getMaxRetries()} must return 0. Default retry interval is 5 seconds. 301 */ 302 protected List<Long> mRetryIntervalsMillis = List.of(TimeUnit.SECONDS.toMillis(5)); 303 304 /** 305 * The maximum retry times. After reaching the retry times, data retry will not be scheduled 306 * with timer. Only environment changes (e.g. Airplane mode, SIM state, RAT, registration 307 * state changes, etc..) can trigger the retry. 308 */ 309 protected int mMaxRetries = 10; 310 311 /** 312 * The network capabilities. Each data setup must be 313 * associated with at least one network request. If that network request contains network 314 * capabilities specified here, then retry will happen. Empty set indicates the retry rule 315 * is not using network capabilities. 316 */ 317 @NonNull 318 @NetCapability 319 protected Set<Integer> mNetworkCapabilities = new ArraySet<>(); 320 321 /** 322 * The fail causes. If data setup failed with certain fail causes, then retry will happen. 323 * Empty set indicates the retry rule is not using the fail causes. 324 */ 325 @NonNull 326 @DataFailureCause 327 protected Set<Integer> mFailCauses = new ArraySet<>(); 328 DataRetryRule(@onNull String ruleString)329 public DataRetryRule(@NonNull String ruleString) { 330 if (TextUtils.isEmpty(ruleString)) { 331 throw new IllegalArgumentException("illegal rule " + ruleString); 332 } 333 ruleString = ruleString.trim().toLowerCase(Locale.ROOT); 334 String[] expressions = ruleString.split("\\s*,\\s*"); 335 for (String expression : expressions) { 336 String[] tokens = expression.trim().split("\\s*=\\s*"); 337 if (tokens.length != 2) { 338 throw new IllegalArgumentException("illegal rule " + ruleString); 339 } 340 String key = tokens[0].trim(); 341 String value = tokens[1].trim(); 342 try { 343 switch (key) { 344 case RULE_TAG_FAIL_CAUSES: 345 mFailCauses = Arrays.stream(value.split("\\s*\\|\\s*")) 346 .map(String::trim) 347 .map(Integer::valueOf) 348 .collect(Collectors.toSet()); 349 break; 350 case RULE_TAG_RETRY_INTERVAL: 351 mRetryIntervalsMillis = Arrays.stream(value.split("\\s*\\|\\s*")) 352 .map(String::trim) 353 .map(Long::valueOf) 354 .collect(Collectors.toList()); 355 break; 356 case RULE_TAG_MAXIMUM_RETRIES: 357 mMaxRetries = Integer.parseInt(value); 358 break; 359 } 360 } catch (Exception e) { 361 e.printStackTrace(); 362 throw new IllegalArgumentException("illegal rule " + ruleString + ", e=" + e); 363 } 364 } 365 366 if (mMaxRetries < 0) { 367 throw new IllegalArgumentException("Max retries should not be less than 0. " 368 + "mMaxRetries=" + mMaxRetries); 369 } 370 371 if (mRetryIntervalsMillis.stream().anyMatch(i -> i <= 0)) { 372 throw new IllegalArgumentException("Retry interval should not be less than 0. " 373 + "mRetryIntervalsMillis=" + mRetryIntervalsMillis); 374 } 375 } 376 377 /** 378 * @return The data network setup retry intervals in milliseconds. If this is empty, then 379 * {@link #getMaxRetries()} must return 0. 380 */ 381 @NonNull getRetryIntervalsMillis()382 public List<Long> getRetryIntervalsMillis() { 383 return mRetryIntervalsMillis; 384 } 385 386 /** 387 * @return The maximum retry times. After reaching the retry times, data retry will not be 388 * scheduled with timer. Only environment changes (e.g. Airplane mode, SIM state, RAT, 389 * registration state changes, etc..) can trigger the retry. Note that if max retries 390 * is 0, then {@link #getRetryIntervalsMillis()} must be {@code null}. 391 */ getMaxRetries()392 public int getMaxRetries() { 393 return mMaxRetries; 394 } 395 396 /** 397 * @return The fail causes. If data setup failed with certain fail causes, then retry will 398 * happen. Empty set indicates the retry rule is not using the fail causes. 399 */ 400 @VisibleForTesting 401 @NonNull 402 @DataFailureCause getFailCauses()403 public Set<Integer> getFailCauses() { 404 return mFailCauses; 405 } 406 } 407 408 /** 409 * Represent a rule for data setup retry. 410 * <p> 411 * The syntax of the retry rule: 412 * 1. Retry based on {@link NetworkCapabilities}. Note that only APN-type network capabilities 413 * are supported. If the capabilities are not specified, then the retry rule only applies 414 * to the current failed APN used in setup data call request. 415 * "capabilities=[netCaps1|netCaps2|...], [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]" 416 * <p> 417 * 2. Retry based on {@link DataFailCause} 418 * "fail_causes=[cause1|cause2|cause3|..], [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]" 419 * <p> 420 * 3. Retry based on {@link NetworkCapabilities} and {@link DataFailCause}. Note that only 421 * APN-type network capabilities are supported. 422 * "capabilities=[netCaps1|netCaps2|...], fail_causes=[cause1|cause2|cause3|...], 423 * [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]" 424 * <p> 425 * 4. Permanent fail causes (no timer-based retry) on the current failed APN. Retry interval 426 * is specified for retrying the next available APN. 427 * "permanent_fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|65543|65547| 428 * 2252|2253|2254, retry_interval=2500" 429 * <p> 430 * For example, 431 * "capabilities=eims, retry_interval=1000, maximum_retries=20" means if the attached 432 * network request is emergency, then retry data network setup every 1 second for up to 20 433 * times. 434 * <p> 435 * "capabilities=internet|enterprise|dun|ims|fota, retry_interval=2500|3000|" 436 * "5000|10000|15000|20000|40000|60000|120000|240000|600000|1200000|1800000" 437 * "1800000, maximum_retries=20" means for those capabilities, retry happens in 2.5s, 3s, 5s, 438 * 10s, 15s, 20s, 40s, 1m, 2m, 4m, 10m, 20m, 30m, 30m, 30m, until reaching 20 retries. 439 */ 440 public static class DataSetupRetryRule extends DataRetryRule { 441 private static final String RULE_TAG_PERMANENT_FAIL_CAUSES = "permanent_fail_causes"; 442 private static final String RULE_TAG_CAPABILITIES = "capabilities"; 443 444 /** {@code true} if this rule is for permanent fail causes. */ 445 private boolean mIsPermanentFailCauseRule; 446 447 /** 448 * Constructor 449 * 450 * @param ruleString The retry rule in string format. 451 * @throws IllegalArgumentException if the string can't be parsed to a retry rule. 452 */ DataSetupRetryRule(@onNull String ruleString)453 public DataSetupRetryRule(@NonNull String ruleString) { 454 super(ruleString); 455 456 ruleString = ruleString.trim().toLowerCase(Locale.ROOT); 457 String[] expressions = ruleString.split("\\s*,\\s*"); 458 for (String expression : expressions) { 459 String[] tokens = expression.trim().split("\\s*=\\s*"); 460 if (tokens.length != 2) { 461 throw new IllegalArgumentException("illegal rule " + ruleString); 462 } 463 String key = tokens[0].trim(); 464 String value = tokens[1].trim(); 465 try { 466 switch (key) { 467 case RULE_TAG_PERMANENT_FAIL_CAUSES: 468 mFailCauses = Arrays.stream(value.split("\\s*\\|\\s*")) 469 .map(String::trim) 470 .map(Integer::valueOf) 471 .collect(Collectors.toSet()); 472 mIsPermanentFailCauseRule = true; 473 break; 474 case RULE_TAG_CAPABILITIES: 475 mNetworkCapabilities = DataUtils 476 .getNetworkCapabilitiesFromString(value); 477 break; 478 } 479 } catch (Exception e) { 480 e.printStackTrace(); 481 throw new IllegalArgumentException("illegal rule " + ruleString + ", e=" + e); 482 } 483 } 484 485 if (mFailCauses.isEmpty() && (mNetworkCapabilities.isEmpty() 486 || mNetworkCapabilities.contains(-1))) { 487 throw new IllegalArgumentException("illegal rule " + ruleString 488 + ". Should have either valid network capabilities or fail causes."); 489 } 490 } 491 492 /** 493 * @return The network capabilities. Each data setup must be associated with at least one 494 * network request. If that network request contains network capabilities specified here, 495 * then retry will happen. Empty set indicates the retry rule is not using network 496 * capabilities. 497 */ 498 @VisibleForTesting 499 @NonNull 500 @NetCapability getNetworkCapabilities()501 public Set<Integer> getNetworkCapabilities() { 502 return mNetworkCapabilities; 503 } 504 505 /** 506 * @return {@code true} if this rule is for permanent fail causes. 507 */ isPermanentFailCauseRule()508 public boolean isPermanentFailCauseRule() { 509 return mIsPermanentFailCauseRule; 510 } 511 512 /** 513 * Check if this rule can be matched. 514 * 515 * @param networkCapability The network capability for matching. 516 * @param cause Fail cause from previous setup data request. 517 * @return {@code true} if the retry rule can be matched. 518 */ canBeMatched(@etCapability int networkCapability, @DataFailureCause int cause)519 public boolean canBeMatched(@NetCapability int networkCapability, 520 @DataFailureCause int cause) { 521 if (!mFailCauses.isEmpty() && !mFailCauses.contains(cause)) { 522 return false; 523 } 524 525 return mNetworkCapabilities.isEmpty() 526 || mNetworkCapabilities.contains(networkCapability); 527 } 528 529 @Override toString()530 public String toString() { 531 return "[DataSetupRetryRule: Network capabilities:" 532 + DataUtils.networkCapabilitiesToString(mNetworkCapabilities.stream() 533 .mapToInt(Number::intValue).toArray()) 534 + ", Fail causes=" + mFailCauses 535 + ", Retry intervals=" + mRetryIntervalsMillis + ", Maximum retries=" 536 + mMaxRetries + "]"; 537 } 538 } 539 540 /** 541 * Represent a handover data network retry rule. 542 * <p> 543 * The syntax of the retry rule: 544 * 1. Retry when handover fails. 545 * "retry_interval=[n1|n2|n3|...], [maximum_retries=n]" 546 * <p> 547 * For example, 548 * "retry_interval=1000|3000|5000, maximum_retries=10" means handover retry will happen in 1s, 549 * 3s, 5s, 5s, 5s....up to 10 times. 550 * <p> 551 * 2. Retry when handover fails with certain fail causes. 552 * "retry_interval=[n1|n2|n3|...], fail_causes=[cause1|cause2|cause3|...], [maximum_retries=n] 553 * <p> 554 * For example, 555 * "retry_interval=1000, maximum_retries=3, fail_causes=5" means handover retry every 1 second 556 * for up to 3 times when handover fails with the cause 5. 557 * <p> 558 * "maximum_retries=0, fail_causes=6|10|67" means handover retry should not happen for those 559 * causes. 560 */ 561 public static class DataHandoverRetryRule extends DataRetryRule { 562 /** 563 * Constructor 564 * 565 * @param ruleString The retry rule in string format. 566 * @throws IllegalArgumentException if the string can't be parsed to a retry rule. 567 */ DataHandoverRetryRule(@onNull String ruleString)568 public DataHandoverRetryRule(@NonNull String ruleString) { 569 super(ruleString); 570 } 571 572 @Override toString()573 public String toString() { 574 return "[DataHandoverRetryRule: Retry intervals=" + mRetryIntervalsMillis 575 + ", Fail causes=" + mFailCauses + ", Maximum retries=" + mMaxRetries + "]"; 576 } 577 } 578 579 /** 580 * Represent a data retry entry. 581 */ 582 public static class DataRetryEntry { 583 /** Indicates the retry is not happened yet. */ 584 public static final int RETRY_STATE_NOT_RETRIED = 1; 585 586 /** Indicates the retry happened, but still failed to setup/handover the data network. */ 587 public static final int RETRY_STATE_FAILED = 2; 588 589 /** Indicates the retry happened and succeeded. */ 590 public static final int RETRY_STATE_SUCCEEDED = 3; 591 592 /** Indicates the retry was cancelled. */ 593 public static final int RETRY_STATE_CANCELLED = 4; 594 595 @IntDef(prefix = {"RETRY_STATE_"}, 596 value = { 597 RETRY_STATE_NOT_RETRIED, 598 RETRY_STATE_FAILED, 599 RETRY_STATE_SUCCEEDED, 600 RETRY_STATE_CANCELLED 601 }) 602 public @interface DataRetryState {} 603 604 /** The rule used for this data retry. {@code null} if the retry is requested by network. */ 605 @Nullable 606 public final DataRetryRule appliedDataRetryRule; 607 608 /** The retry delay in milliseconds. */ 609 public final long retryDelayMillis; 610 611 /** 612 * Retry elapsed time. This is the system elapsed time retrieved from 613 * {@link SystemClock#elapsedRealtime()}. 614 */ 615 @ElapsedRealtimeLong 616 public final long retryElapsedTime; 617 618 /** The retry state. */ 619 protected int mRetryState = RETRY_STATE_NOT_RETRIED; 620 621 /** Timestamp when a state is set. For debugging purposes only. */ 622 @ElapsedRealtimeLong 623 protected long mRetryStateTimestamp; 624 625 /** 626 * Constructor 627 * 628 * @param appliedDataRetryRule The applied data retry rule. 629 * @param retryDelayMillis The retry delay in milliseconds. 630 */ DataRetryEntry(@ullable DataRetryRule appliedDataRetryRule, long retryDelayMillis)631 public DataRetryEntry(@Nullable DataRetryRule appliedDataRetryRule, long retryDelayMillis) { 632 this.appliedDataRetryRule = appliedDataRetryRule; 633 this.retryDelayMillis = retryDelayMillis; 634 635 mRetryStateTimestamp = SystemClock.elapsedRealtime(); 636 retryElapsedTime = mRetryStateTimestamp + retryDelayMillis; 637 } 638 639 /** 640 * Set the state of a data retry. 641 * 642 * @param state The retry state. 643 */ setState(@ataRetryState int state)644 public void setState(@DataRetryState int state) { 645 mRetryState = state; 646 mRetryStateTimestamp = SystemClock.elapsedRealtime(); 647 } 648 649 /** 650 * @return Get the retry state. 651 */ 652 @DataRetryState getState()653 public int getState() { 654 return mRetryState; 655 } 656 657 /** 658 * Convert retry state to string. 659 * 660 * @param retryState Retry state. 661 * @return Retry state in string format. 662 */ retryStateToString(@ataRetryState int retryState)663 public static String retryStateToString(@DataRetryState int retryState) { 664 return switch (retryState) { 665 case RETRY_STATE_NOT_RETRIED -> "NOT_RETRIED"; 666 case RETRY_STATE_FAILED -> "FAILED"; 667 case RETRY_STATE_SUCCEEDED -> "SUCCEEDED"; 668 case RETRY_STATE_CANCELLED -> "CANCELLED"; 669 default -> "Unknown(" + retryState + ")"; 670 }; 671 } 672 673 /** 674 * The generic builder for retry entry. 675 * 676 * @param <T> The type of extended retry entry builder. 677 */ 678 public static class Builder<T extends Builder<T>> { 679 /** 680 * The retry delay in milliseconds. Default is 5 seconds. 681 */ 682 protected long mRetryDelayMillis = TimeUnit.SECONDS.toMillis(5); 683 684 /** The applied data retry rule. */ 685 @Nullable 686 protected DataRetryRule mAppliedDataRetryRule; 687 688 /** 689 * Set the data retry delay. 690 * 691 * @param retryDelayMillis The retry delay in milliseconds. 692 * @return This builder. 693 */ 694 @NonNull setRetryDelay(long retryDelayMillis)695 public T setRetryDelay(long retryDelayMillis) { 696 mRetryDelayMillis = retryDelayMillis; 697 return (T) this; 698 } 699 700 /** 701 * Set the applied retry rule. 702 * 703 * @param dataRetryRule The rule that used for this data retry. 704 * @return This builder. 705 */ 706 @NonNull setAppliedRetryRule(@onNull DataRetryRule dataRetryRule)707 public T setAppliedRetryRule(@NonNull DataRetryRule dataRetryRule) { 708 mAppliedDataRetryRule = dataRetryRule; 709 return (T) this; 710 } 711 } 712 } 713 714 /** 715 * Represent a setup data retry entry. 716 */ 717 public static class DataSetupRetryEntry extends DataRetryEntry { 718 /** 719 * Retry type is unknown. This should be only used for initialized value. 720 */ 721 public static final int RETRY_TYPE_UNKNOWN = 0; 722 /** 723 * To retry setup data with the same data profile. 724 */ 725 public static final int RETRY_TYPE_DATA_PROFILE = 1; 726 727 /** 728 * To retry satisfying the network request(s). Could be using a 729 * different data profile. 730 */ 731 public static final int RETRY_TYPE_NETWORK_REQUESTS = 2; 732 733 @IntDef(prefix = {"RETRY_TYPE_"}, 734 value = { 735 RETRY_TYPE_UNKNOWN, 736 RETRY_TYPE_DATA_PROFILE, 737 RETRY_TYPE_NETWORK_REQUESTS, 738 }) 739 public @interface SetupRetryType {} 740 741 /** Setup retry type. Could be retry by same data profile or same capability. */ 742 @SetupRetryType 743 public final int setupRetryType; 744 745 /** The network requests to satisfy when retry happens. */ 746 @NonNull 747 public final NetworkRequestList networkRequestList; 748 749 /** The data profile that will be used for retry. */ 750 @Nullable 751 public final DataProfile dataProfile; 752 753 /** The transport to retry data setup. */ 754 @TransportType 755 public final int transport; 756 757 /** 758 * Constructor 759 * 760 * @param setupRetryType Data retry type. Could be retry by same data profile or same 761 * capabilities. 762 * @param networkRequestList The network requests to satisfy when retry happens. 763 * @param dataProfile The data profile that will be used for retry. 764 * @param transport The transport to retry data setup. 765 * @param appliedDataSetupRetryRule The applied data setup retry rule. 766 * @param retryDelayMillis The retry delay in milliseconds. 767 */ DataSetupRetryEntry(@etupRetryType int setupRetryType, @Nullable NetworkRequestList networkRequestList, @NonNull DataProfile dataProfile, @TransportType int transport, @Nullable DataSetupRetryRule appliedDataSetupRetryRule, long retryDelayMillis)768 private DataSetupRetryEntry(@SetupRetryType int setupRetryType, 769 @Nullable NetworkRequestList networkRequestList, @NonNull DataProfile dataProfile, 770 @TransportType int transport, 771 @Nullable DataSetupRetryRule appliedDataSetupRetryRule, long retryDelayMillis) { 772 super(appliedDataSetupRetryRule, retryDelayMillis); 773 this.setupRetryType = setupRetryType; 774 this.networkRequestList = networkRequestList; 775 this.dataProfile = dataProfile; 776 this.transport = transport; 777 } 778 779 /** 780 * Convert retry type to string. 781 * 782 * @param setupRetryType Data setup retry type. 783 * @return Retry type in string format. 784 */ retryTypeToString(@etupRetryType int setupRetryType)785 private static String retryTypeToString(@SetupRetryType int setupRetryType) { 786 return switch (setupRetryType) { 787 case RETRY_TYPE_DATA_PROFILE -> "BY_PROFILE"; 788 case RETRY_TYPE_NETWORK_REQUESTS -> "BY_NETWORK_REQUESTS"; 789 case RETRY_TYPE_UNKNOWN -> "UNKNOWN"; 790 default -> "Unknown(" + setupRetryType + ")"; 791 }; 792 } 793 794 @Override toString()795 public String toString() { 796 return "[DataSetupRetryEntry: delay=" + retryDelayMillis + "ms, retry time:" 797 + DataUtils.elapsedTimeToString(retryElapsedTime) + ", " + dataProfile 798 + ", transport=" + AccessNetworkConstants.transportTypeToString(transport) 799 + ", retry type=" + retryTypeToString(setupRetryType) + ", retry requests=" 800 + networkRequestList + ", applied rule=" + appliedDataRetryRule + ", state=" 801 + retryStateToString(mRetryState) + ", timestamp=" 802 + DataUtils.elapsedTimeToString(mRetryStateTimestamp) + "]"; 803 } 804 805 /** 806 * The builder of {@link DataSetupRetryEntry}. 807 * 808 * @param <T> Type of the builder. 809 */ 810 public static class Builder<T extends Builder<T>> extends DataRetryEntry.Builder<T> { 811 /** Data setup retry type. Could be retry by same data profile or same capabilities. */ 812 @SetupRetryType 813 private int mSetupRetryType = RETRY_TYPE_UNKNOWN; 814 815 /** The network requests to satisfy when retry happens. */ 816 @NonNull 817 private NetworkRequestList mNetworkRequestList; 818 819 /** The data profile that will be used for retry. */ 820 @Nullable 821 private DataProfile mDataProfile; 822 823 /** The transport to retry data setup. */ 824 @TransportType 825 private int mTransport = AccessNetworkConstants.TRANSPORT_TYPE_INVALID; 826 827 /** 828 * Set the data retry type. 829 * 830 * @param setupRetryType Data retry type. Could be retry by same data profile or same 831 * capabilities. 832 * @return This builder. 833 */ 834 @NonNull setSetupRetryType(@etupRetryType int setupRetryType)835 public Builder<T> setSetupRetryType(@SetupRetryType int setupRetryType) { 836 mSetupRetryType = setupRetryType; 837 return this; 838 } 839 840 /** 841 * Set the network capability to satisfy when retry happens. 842 * 843 * @param networkRequestList The network requests to satisfy when retry happens. 844 * @return This builder. 845 */ 846 @NonNull setNetworkRequestList( @onNull NetworkRequestList networkRequestList)847 public Builder<T> setNetworkRequestList( 848 @NonNull NetworkRequestList networkRequestList) { 849 mNetworkRequestList = networkRequestList; 850 return this; 851 } 852 853 /** 854 * Set the data profile that will be used for retry. 855 * 856 * @param dataProfile The data profile that will be used for retry. 857 * @return This builder. 858 */ 859 @NonNull setDataProfile(@onNull DataProfile dataProfile)860 public Builder<T> setDataProfile(@NonNull DataProfile dataProfile) { 861 mDataProfile = dataProfile; 862 return this; 863 } 864 865 /** 866 * Set the transport of the data setup retry. 867 * 868 * @param transport The transport to retry data setup. 869 * @return This builder. 870 */ 871 @NonNull setTransport(@ransportType int transport)872 public Builder<T> setTransport(@TransportType int transport) { 873 mTransport = transport; 874 return this; 875 } 876 877 /** 878 * Build the instance of {@link DataSetupRetryEntry}. 879 * 880 * @return The instance of {@link DataSetupRetryEntry}. 881 */ 882 @NonNull build()883 public DataSetupRetryEntry build() { 884 if (mNetworkRequestList == null) { 885 throw new IllegalArgumentException("network request list is not specified."); 886 } 887 if (mTransport != AccessNetworkConstants.TRANSPORT_TYPE_WWAN 888 && mTransport != AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { 889 throw new IllegalArgumentException("Invalid transport type " + mTransport); 890 } 891 if (mSetupRetryType != RETRY_TYPE_DATA_PROFILE 892 && mSetupRetryType != RETRY_TYPE_NETWORK_REQUESTS) { 893 throw new IllegalArgumentException("Invalid setup retry type " 894 + mSetupRetryType); 895 } 896 return new DataSetupRetryEntry(mSetupRetryType, mNetworkRequestList, mDataProfile, 897 mTransport, (DataSetupRetryRule) mAppliedDataRetryRule, mRetryDelayMillis); 898 } 899 } 900 } 901 902 /** 903 * Represent a data handover retry entry. 904 */ 905 public static class DataHandoverRetryEntry extends DataRetryEntry { 906 /** The data network to be retried for handover. */ 907 @NonNull 908 public final DataNetwork dataNetwork; 909 910 /** 911 * Constructor. 912 * 913 * @param dataNetwork The data network to be retried for handover. 914 * @param appliedDataHandoverRetryRule The applied data retry rule. 915 * @param retryDelayMillis The retry delay in milliseconds. 916 */ 917 public DataHandoverRetryEntry(@NonNull DataNetwork dataNetwork, 918 @Nullable DataHandoverRetryRule appliedDataHandoverRetryRule, 919 long retryDelayMillis) { 920 super(appliedDataHandoverRetryRule, retryDelayMillis); 921 this.dataNetwork = dataNetwork; 922 } 923 924 @Override 925 public String toString() { 926 return "[DataHandoverRetryEntry: delay=" + retryDelayMillis + "ms, retry time:" 927 + DataUtils.elapsedTimeToString(retryElapsedTime) + ", " + dataNetwork 928 + ", applied rule=" + appliedDataRetryRule + ", state=" 929 + retryStateToString(mRetryState) + ", timestamp=" 930 + DataUtils.elapsedTimeToString(mRetryStateTimestamp) + "]"; 931 } 932 933 /** 934 * The builder of {@link DataHandoverRetryEntry}. 935 * 936 * @param <T> Type of the builder. 937 */ 938 public static class Builder<T extends Builder<T>> extends DataRetryEntry.Builder<T> { 939 /** The data network to be retried for handover. */ 940 @NonNull 941 public DataNetwork mDataNetwork; 942 943 /** 944 * Set the data retry type. 945 * 946 * @param dataNetwork The data network to be retried for handover. 947 * 948 * @return This builder. 949 */ 950 @NonNull 951 public Builder<T> setDataNetwork(@NonNull DataNetwork dataNetwork) { 952 mDataNetwork = dataNetwork; 953 return this; 954 } 955 956 /** 957 * Build the instance of {@link DataHandoverRetryEntry}. 958 * 959 * @return The instance of {@link DataHandoverRetryEntry}. 960 */ 961 @NonNull 962 public DataHandoverRetryEntry build() { 963 return new DataHandoverRetryEntry(mDataNetwork, 964 (DataHandoverRetryRule) mAppliedDataRetryRule, mRetryDelayMillis); 965 } 966 } 967 } 968 969 /** Data retry callback. */ 970 public static class DataRetryManagerCallback extends DataCallback { 971 /** 972 * Constructor 973 * 974 * @param executor The executor of the callback. 975 */ 976 public DataRetryManagerCallback(@NonNull @CallbackExecutor Executor executor) { 977 super(executor); 978 } 979 980 /** 981 * Called when data setup retry occurs. 982 * 983 * @param dataSetupRetryEntry The data setup retry entry. 984 */ 985 public void onDataNetworkSetupRetry(@NonNull DataSetupRetryEntry dataSetupRetryEntry) {} 986 987 /** 988 * Called when data handover retry occurs. 989 * 990 * @param dataHandoverRetryEntry The data handover retry entry. 991 */ 992 public void onDataNetworkHandoverRetry( 993 @NonNull DataHandoverRetryEntry dataHandoverRetryEntry) {} 994 995 /** 996 * Called when retry manager determines that the retry will no longer be performed on 997 * this data network. 998 * 999 * @param dataNetwork The data network that will never be retried handover. 1000 */ 1001 public void onDataNetworkHandoverRetryStopped(@NonNull DataNetwork dataNetwork) {} 1002 1003 /** 1004 * Called when throttle status changed reported from network. 1005 * 1006 * @param throttleStatusList List of throttle status. 1007 */ 1008 public void onThrottleStatusChanged(@NonNull List<ThrottleStatus> throttleStatusList) {} 1009 } 1010 1011 /** 1012 * Constructor 1013 * 1014 * @param phone The phone instance. 1015 * @param dataNetworkController Data network controller. 1016 * @param looper The looper to be used by the handler. Currently the handler thread is the 1017 * phone process's main thread. 1018 * @param dataRetryManagerCallback Data retry callback. 1019 */ 1020 public DataRetryManager(@NonNull Phone phone, 1021 @NonNull DataNetworkController dataNetworkController, 1022 @NonNull SparseArray<DataServiceManager> dataServiceManagers, 1023 @NonNull Looper looper, @NonNull FeatureFlags flags, 1024 @NonNull DataRetryManagerCallback dataRetryManagerCallback) { 1025 super(looper); 1026 mPhone = phone; 1027 mRil = phone.mCi; 1028 mFlags = flags; 1029 mLogTag = "DRM-" + mPhone.getPhoneId(); 1030 mDataRetryManagerCallbacks.add(dataRetryManagerCallback); 1031 1032 mDataServiceManagers = dataServiceManagers; 1033 mDataConfigManager = dataNetworkController.getDataConfigManager(); 1034 mDataProfileManager = dataNetworkController.getDataProfileManager(); 1035 mAlarmManager = mPhone.getContext().getSystemService(AlarmManager.class); 1036 1037 mDataConfigManager.registerCallback(new DataConfigManagerCallback(this::post) { 1038 @Override 1039 public void onCarrierConfigChanged() { 1040 DataRetryManager.this.onCarrierConfigUpdated(); 1041 } 1042 }); 1043 1044 for (int transport : mPhone.getAccessNetworksManager().getAvailableTransports()) { 1045 mDataServiceManagers.get(transport) 1046 .registerForApnUnthrottled(this, EVENT_DATA_PROFILE_UNTHROTTLED); 1047 } 1048 mDataProfileManager.registerCallback(new DataProfileManagerCallback(this::post) { 1049 @Override 1050 public void onDataProfilesChanged() { 1051 onReset(RESET_REASON_DATA_PROFILES_CHANGED); 1052 } 1053 }); 1054 dataNetworkController.registerDataNetworkControllerCallback( 1055 new DataNetworkControllerCallback(this::post) { 1056 /** 1057 * Called when data service is bound. 1058 * 1059 * @param transport The transport of the data service. 1060 */ 1061 @Override 1062 public void onDataServiceBound(@TransportType int transport) { 1063 onReset(RESET_REASON_DATA_SERVICE_BOUND); 1064 } 1065 1066 /** 1067 * Called when data network is connected. 1068 * 1069 * @param transport Transport for the connected network. 1070 * @param dataProfile The data profile of the connected data network. 1071 */ 1072 @Override 1073 public void onDataNetworkConnected(@TransportType int transport, 1074 @NonNull DataProfile dataProfile) { 1075 DataRetryManager.this.onDataNetworkConnected(transport, dataProfile); 1076 } 1077 }); 1078 mRil.registerForOn(this, EVENT_RADIO_ON, null); 1079 mRil.registerForModemReset(this, EVENT_MODEM_RESET, null); 1080 1081 if (!mFlags.useAlarmCallback()) { 1082 // Register intent of alarm manager for long retry timer 1083 IntentFilter intentFilter = new IntentFilter(); 1084 intentFilter.addAction(ACTION_RETRY); 1085 mPhone.getContext().registerReceiver(new BroadcastReceiver() { 1086 @Override 1087 public void onReceive(Context context, Intent intent) { 1088 if (ACTION_RETRY.equals(intent.getAction())) { 1089 DataRetryManager.this.onAlarmIntentRetry( 1090 intent.getIntExtra(ACTION_RETRY_EXTRA_HASHCODE, 1091 -1 /*Bad hashcode*/)); 1092 } 1093 } 1094 }, intentFilter); 1095 } 1096 1097 if (mDataConfigManager.shouldResetDataThrottlingWhenTacChanges()) { 1098 mPhone.getServiceStateTracker().registerForAreaCodeChanged(this, EVENT_TAC_CHANGED, 1099 null); 1100 } 1101 } 1102 1103 @Override 1104 public void handleMessage(Message msg) { 1105 AsyncResult ar; 1106 switch (msg.what) { 1107 case EVENT_DATA_SETUP_RETRY: 1108 DataSetupRetryEntry dataSetupRetryEntry = (DataSetupRetryEntry) msg.obj; 1109 if (!isRetryCancelled(dataSetupRetryEntry)) { 1110 mDataRetryManagerCallbacks.forEach(callback -> callback.invokeFromExecutor( 1111 () -> callback.onDataNetworkSetupRetry(dataSetupRetryEntry))); 1112 } 1113 break; 1114 case EVENT_DATA_HANDOVER_RETRY: 1115 DataHandoverRetryEntry dataHandoverRetryEntry = (DataHandoverRetryEntry) msg.obj; 1116 if (!isRetryCancelled(dataHandoverRetryEntry)) { 1117 mDataRetryManagerCallbacks.forEach(callback -> callback.invokeFromExecutor( 1118 () -> callback.onDataNetworkHandoverRetry(dataHandoverRetryEntry))); 1119 } 1120 break; 1121 case EVENT_RADIO_ON: 1122 onReset(RESET_REASON_RADIO_ON); 1123 break; 1124 case EVENT_MODEM_RESET: 1125 onReset(RESET_REASON_MODEM_RESTART); 1126 break; 1127 case EVENT_TAC_CHANGED: 1128 onReset(RESET_REASON_TAC_CHANGED); 1129 break; 1130 case EVENT_DATA_PROFILE_UNTHROTTLED: 1131 ar = (AsyncResult) msg.obj; 1132 int transport = (int) ar.userObj; 1133 String apn = null; 1134 DataProfile dataProfile = null; 1135 // 1.6 or older HAL 1136 if (ar.result instanceof String) { 1137 apn = (String) ar.result; 1138 } else if (ar.result instanceof DataProfile) { 1139 dataProfile = (DataProfile) ar.result; 1140 } 1141 onDataProfileUnthrottled(dataProfile, apn, transport, true, true); 1142 break; 1143 case EVENT_CANCEL_PENDING_HANDOVER_RETRY: 1144 onCancelPendingHandoverRetry((DataNetwork) msg.obj); 1145 break; 1146 default: 1147 loge("Unexpected message " + msg.what); 1148 } 1149 } 1150 1151 /** 1152 * @param retryEntry The retry entry to check. 1153 * @return {@code true} if the retry is null or not in RETRY_STATE_NOT_RETRIED state. 1154 */ 1155 private boolean isRetryCancelled(@Nullable DataRetryEntry retryEntry) { 1156 if (retryEntry != null && retryEntry.getState() == DataRetryEntry.RETRY_STATE_NOT_RETRIED) { 1157 return false; 1158 } 1159 log("Retry was removed earlier. " + retryEntry); 1160 return true; 1161 } 1162 1163 /** 1164 * Called when carrier config is updated. 1165 */ 1166 private void onCarrierConfigUpdated() { 1167 onReset(RESET_REASON_DATA_CONFIG_CHANGED); 1168 mDataSetupRetryRuleList = mDataConfigManager.getDataSetupRetryRules(); 1169 mDataHandoverRetryRuleList = mDataConfigManager.getDataHandoverRetryRules(); 1170 log("onDataConfigUpdated: mDataSetupRetryRuleList=" + mDataSetupRetryRuleList 1171 + ", mDataHandoverRetryRuleList=" + mDataHandoverRetryRuleList); 1172 } 1173 1174 /** 1175 * Called when data network is connected. 1176 * 1177 * @param transport Transport for the connected network. 1178 * @param dataProfile The data profile of the connected data network. 1179 */ 1180 public void onDataNetworkConnected(@TransportType int transport, 1181 @NonNull DataProfile dataProfile) { 1182 if (dataProfile.getApnSetting() != null) { 1183 dataProfile.getApnSetting().setPermanentFailed(false); 1184 } 1185 1186 onDataProfileUnthrottled(dataProfile, null, transport, true, false); 1187 } 1188 1189 /** 1190 * Evaluate if data setup retry is needed or not. If needed, retry will be scheduled 1191 * automatically after evaluation. 1192 * 1193 * @param dataProfile The data profile that has been used in the previous data network setup. 1194 * @param transport The transport to retry data setup. 1195 * @param requestList The network requests attached to the previous data network setup. 1196 * @param cause The fail cause of previous data network setup. 1197 * @param retryDelayMillis The retry delay in milliseconds suggested by the network/data 1198 * service. {@link android.telephony.data.DataCallResponse#RETRY_DURATION_UNDEFINED} 1199 * indicates network/data service did not suggest retry or not. Telephony frameworks would use 1200 * its logic to perform data retry. 1201 */ 1202 public void evaluateDataSetupRetry(@NonNull DataProfile dataProfile, 1203 @TransportType int transport, @NonNull NetworkRequestList requestList, 1204 @DataFailureCause int cause, long retryDelayMillis) { 1205 post(() -> onEvaluateDataSetupRetry(dataProfile, transport, requestList, cause, 1206 retryDelayMillis)); 1207 } 1208 1209 private void onEvaluateDataSetupRetry(@NonNull DataProfile dataProfile, 1210 @TransportType int transport, @NonNull NetworkRequestList requestList, 1211 @DataFailureCause int cause, long retryDelayMillis) { 1212 logl("onEvaluateDataSetupRetry: " + dataProfile + ", transport=" 1213 + AccessNetworkConstants.transportTypeToString(transport) + ", cause=" 1214 + DataFailCause.toString(cause) + ", retryDelayMillis=" + retryDelayMillis + "ms" 1215 + ", " + requestList); 1216 // Check if network suggested never retry for this data profile. Note that for HAL 1.5 1217 // and older, Integer.MAX_VALUE indicates never retry. For 1.6 or above, Long.MAX_VALUE 1218 // indicates never retry. 1219 if (retryDelayMillis == Long.MAX_VALUE || retryDelayMillis == Integer.MAX_VALUE) { 1220 logl("Network suggested never retry for " + dataProfile); 1221 // Note that RETRY_TYPE_NEW_CONNECTION is intended here because 1222 // when unthrottling happens, we still want to retry and we'll need 1223 // a type there so we know what to retry. Using RETRY_TYPE_NONE 1224 // ThrottleStatus is just for API backwards compatibility reason. 1225 throttleDataProfile(dataProfile, requestList, null, 1226 ThrottleStatus.RETRY_TYPE_NEW_CONNECTION, transport, Long.MAX_VALUE); 1227 return; 1228 } else if (retryDelayMillis != DataCallResponse.RETRY_DURATION_UNDEFINED) { 1229 // Network specifically asks retry the previous data profile again. 1230 DataSetupRetryEntry dataSetupRetryEntry = new DataSetupRetryEntry.Builder<>() 1231 .setRetryDelay(retryDelayMillis) 1232 .setSetupRetryType(DataSetupRetryEntry.RETRY_TYPE_DATA_PROFILE) 1233 .setNetworkRequestList(requestList) 1234 .setDataProfile(dataProfile) 1235 .setTransport(transport) 1236 .build(); 1237 throttleDataProfile(dataProfile, requestList, null, 1238 ThrottleStatus.RETRY_TYPE_NEW_CONNECTION, transport, 1239 dataSetupRetryEntry.retryElapsedTime); 1240 schedule(dataSetupRetryEntry); 1241 return; 1242 } 1243 1244 // Network did not suggest any retry. Use the configured rules to perform retry. 1245 logv("mDataSetupRetryRuleList=" + mDataSetupRetryRuleList); 1246 1247 boolean retryScheduled = false; 1248 List<NetworkRequestList> groupedNetworkRequestLists = 1249 DataUtils.getGroupedNetworkRequestList(requestList, mFlags); 1250 for (DataSetupRetryRule retryRule : mDataSetupRetryRuleList) { 1251 if (retryRule.isPermanentFailCauseRule() && retryRule.getFailCauses().contains(cause)) { 1252 if (dataProfile.getApnSetting() != null) { 1253 dataProfile.getApnSetting().setPermanentFailed(true); 1254 1255 // It seems strange to have retry timer in permanent failure rule, but since 1256 // in this case permanent failure is only applicable to the failed profile, so 1257 // we need to see if other profile can be selected for next data setup. 1258 log("Marked " + dataProfile.getApnSetting().getApnName() + " permanently " 1259 + "failed, but still schedule retry to see if another data profile " 1260 + "can be used for setup data."); 1261 // Schedule a data retry to see if another data profile could be selected. 1262 // If the same data profile is selected again, since it's marked as 1263 // permanent failure, it won't be used for setup data call. 1264 schedule(new DataSetupRetryEntry.Builder<>() 1265 .setRetryDelay(retryRule.getRetryIntervalsMillis().get(0)) 1266 .setAppliedRetryRule(retryRule) 1267 .setSetupRetryType(DataSetupRetryEntry.RETRY_TYPE_NETWORK_REQUESTS) 1268 .setTransport(transport) 1269 .setNetworkRequestList(requestList) 1270 .build()); 1271 } else { 1272 // For TD-based data profile, do not do anything for now. Should expand this in 1273 // the future if needed. 1274 log("Stopped timer-based retry for TD-based data profile. Will retry only when " 1275 + "environment changes."); 1276 } 1277 return; 1278 } 1279 for (NetworkRequestList networkRequestList : groupedNetworkRequestLists) { 1280 int capability = networkRequestList.get(0).getApnTypeNetworkCapability(); 1281 if (retryRule.canBeMatched(capability, cause)) { 1282 // Check if there is already a similar network request retry scheduled. 1283 if (isSimilarNetworkRequestRetryScheduled( 1284 networkRequestList.get(0), transport)) { 1285 log(networkRequestList.get(0) + " already had similar retry " 1286 + "scheduled."); 1287 return; 1288 } 1289 1290 int failedCount = getRetryFailedCount(capability, retryRule, transport); 1291 log("For capability " + DataUtils.networkCapabilityToString(capability) 1292 + ", found matching rule " + retryRule + ", failed count=" 1293 + failedCount); 1294 if (failedCount == retryRule.getMaxRetries()) { 1295 log("Data retry failed for " + failedCount + " times on " 1296 + AccessNetworkConstants.transportTypeToString(transport) 1297 + ". Stopped timer-based data retry for " 1298 + DataUtils.networkCapabilityToString(capability) 1299 + ". Condition-based retry will still happen when condition " 1300 + "changes."); 1301 return; 1302 } 1303 1304 retryDelayMillis = retryRule.getRetryIntervalsMillis().get( 1305 Math.min(failedCount, retryRule 1306 .getRetryIntervalsMillis().size() - 1)); 1307 1308 // Schedule a data retry. 1309 schedule(new DataSetupRetryEntry.Builder<>() 1310 .setRetryDelay(retryDelayMillis) 1311 .setAppliedRetryRule(retryRule) 1312 .setSetupRetryType(DataSetupRetryEntry.RETRY_TYPE_NETWORK_REQUESTS) 1313 .setTransport(transport) 1314 .setNetworkRequestList(networkRequestList) 1315 .build()); 1316 retryScheduled = true; 1317 break; 1318 } 1319 } 1320 } 1321 1322 if (!retryScheduled) { 1323 log("onEvaluateDataSetupRetry: Did not match any retry rule. Stop timer-based " 1324 + "retry."); 1325 } 1326 } 1327 1328 /** 1329 * Evaluate if data handover retry is needed or not. If needed, retry will be scheduled 1330 * automatically after evaluation. 1331 * 1332 * @param dataNetwork The data network to be retried for handover. 1333 * @param cause The fail cause of previous data network handover. 1334 * @param retryDelayMillis The retry delay in milliseconds suggested by the network/data 1335 * service. {@link android.telephony.data.DataCallResponse#RETRY_DURATION_UNDEFINED} 1336 * indicates network/data service did not suggest retry or not. Telephony frameworks would use 1337 * its logic to perform handover retry. 1338 */ 1339 public void evaluateDataHandoverRetry(@NonNull DataNetwork dataNetwork, 1340 @DataFailureCause int cause, long retryDelayMillis) { 1341 post(() -> onEvaluateDataHandoverRetry(dataNetwork, cause, retryDelayMillis)); 1342 } 1343 1344 private void onEvaluateDataHandoverRetry(@NonNull DataNetwork dataNetwork, 1345 @DataFailureCause int cause, long retryDelayMillis) { 1346 logl("onEvaluateDataHandoverRetry: " + dataNetwork + ", cause=" 1347 + DataFailCause.toString(cause) + ", retryDelayMillis=" + retryDelayMillis + "ms"); 1348 int targetTransport = DataUtils.getTargetTransport(dataNetwork.getTransport()); 1349 if (retryDelayMillis == Long.MAX_VALUE || retryDelayMillis == Integer.MAX_VALUE) { 1350 logl("Network suggested never retry handover for " + dataNetwork); 1351 // Note that RETRY_TYPE_HANDOVER is intended here because 1352 // when unthrottling happens, we still want to retry and we'll need 1353 // a type there so we know what to retry. Using RETRY_TYPE_NONE 1354 // ThrottleStatus is just for API backwards compatibility reason. 1355 throttleDataProfile(dataNetwork.getDataProfile(), 1356 dataNetwork.getAttachedNetworkRequestList(), dataNetwork, 1357 ThrottleStatus.RETRY_TYPE_HANDOVER, targetTransport, Long.MAX_VALUE); 1358 } else if (retryDelayMillis != DataCallResponse.RETRY_DURATION_UNDEFINED) { 1359 // Network specifically asks retry the previous data profile again. 1360 DataHandoverRetryEntry dataHandoverRetryEntry = new DataHandoverRetryEntry.Builder<>() 1361 .setRetryDelay(retryDelayMillis) 1362 .setDataNetwork(dataNetwork) 1363 .build(); 1364 1365 throttleDataProfile(dataNetwork.getDataProfile(), 1366 dataNetwork.getAttachedNetworkRequestList(), dataNetwork, 1367 ThrottleStatus.RETRY_TYPE_HANDOVER, targetTransport, 1368 dataHandoverRetryEntry.retryElapsedTime); 1369 schedule(dataHandoverRetryEntry); 1370 } else { 1371 // Network did not suggest any retry. Use the configured rules to perform retry. 1372 1373 // Matching the rule in configured order. 1374 for (DataHandoverRetryRule retryRule : mDataHandoverRetryRuleList) { 1375 if (retryRule.getFailCauses().isEmpty() 1376 || retryRule.getFailCauses().contains(cause)) { 1377 int failedCount = getRetryFailedCount(dataNetwork, retryRule); 1378 log("Found matching rule " + retryRule + ", failed count=" + failedCount); 1379 if (failedCount == retryRule.getMaxRetries()) { 1380 log("Data handover retry failed for " + failedCount + " times. Stopped " 1381 + "handover retry."); 1382 mDataRetryManagerCallbacks.forEach(callback -> callback.invokeFromExecutor( 1383 () -> callback.onDataNetworkHandoverRetryStopped(dataNetwork))); 1384 return; 1385 } 1386 1387 retryDelayMillis = retryRule.getRetryIntervalsMillis().get( 1388 Math.min(failedCount, retryRule 1389 .getRetryIntervalsMillis().size() - 1)); 1390 schedule(new DataHandoverRetryEntry.Builder<>() 1391 .setRetryDelay(retryDelayMillis) 1392 .setDataNetwork(dataNetwork) 1393 .setAppliedRetryRule(retryRule) 1394 .build()); 1395 } 1396 } 1397 } 1398 } 1399 1400 /** 1401 * @param dataNetwork The data network to check. 1402 * @return {@code true} if the data network had failed the maximum number of attempts for 1403 * handover according to any retry rules. 1404 */ 1405 public boolean isDataNetworkHandoverRetryStopped(@NonNull DataNetwork dataNetwork) { 1406 // Matching the rule in configured order. 1407 for (DataHandoverRetryRule retryRule : mDataHandoverRetryRuleList) { 1408 int failedCount = getRetryFailedCount(dataNetwork, retryRule); 1409 if (failedCount == retryRule.getMaxRetries()) { 1410 log("Data handover retry failed for " + failedCount + " times. Stopped " 1411 + "handover retry."); 1412 return true; 1413 } 1414 } 1415 return false; 1416 } 1417 1418 /** Cancel all retries and throttling entries. */ 1419 private void onReset(@RetryResetReason int reason) { 1420 logl("Remove all retry and throttling entries, reason=" + resetReasonToString(reason)); 1421 removeMessages(EVENT_DATA_SETUP_RETRY); 1422 removeMessages(EVENT_DATA_HANDOVER_RETRY); 1423 1424 mDataProfileManager.clearAllDataProfilePermanentFailures(); 1425 1426 mDataRetryEntries.stream() 1427 .filter(entry -> entry.getState() == DataRetryEntry.RETRY_STATE_NOT_RETRIED) 1428 .forEach(entry -> entry.setState(DataRetryEntry.RETRY_STATE_CANCELLED)); 1429 1430 for (DataThrottlingEntry dataThrottlingEntry : mDataThrottlingEntries) { 1431 DataProfile dataProfile = dataThrottlingEntry.dataProfile; 1432 String apn = dataProfile.getApnSetting() != null 1433 ? dataProfile.getApnSetting().getApnName() : null; 1434 onDataProfileUnthrottled(dataProfile, apn, dataThrottlingEntry.transport, false, true); 1435 } 1436 1437 mDataThrottlingEntries.clear(); 1438 } 1439 1440 /** 1441 * Count how many times the same setup retry rule has been used for this data network but 1442 * failed. 1443 * 1444 * @param dataNetwork The data network to check. 1445 * @param dataRetryRule The data retry rule. 1446 * @return The failed count since last successful data setup. 1447 */ 1448 private int getRetryFailedCount(@NonNull DataNetwork dataNetwork, 1449 @NonNull DataHandoverRetryRule dataRetryRule) { 1450 int count = 0; 1451 for (int i = mDataRetryEntries.size() - 1; i >= 0; i--) { 1452 if (mDataRetryEntries.get(i) instanceof DataHandoverRetryEntry) { 1453 DataHandoverRetryEntry entry = (DataHandoverRetryEntry) mDataRetryEntries.get(i); 1454 if (entry.dataNetwork == dataNetwork 1455 && dataRetryRule.equals(entry.appliedDataRetryRule)) { 1456 if (entry.getState() == DataRetryEntry.RETRY_STATE_SUCCEEDED 1457 || entry.getState() == DataRetryEntry.RETRY_STATE_CANCELLED) { 1458 break; 1459 } 1460 count++; 1461 } 1462 } 1463 } 1464 return count; 1465 } 1466 1467 /** 1468 * Count how many times the same setup retry rule has been used for the capability since 1469 * last success data setup. 1470 * 1471 * @param networkCapability The network capability to check. 1472 * @param dataRetryRule The data retry rule. 1473 * @param transport The transport on which setup failure has occurred. 1474 * @return The failed count since last successful data setup. 1475 */ 1476 private int getRetryFailedCount(@NetCapability int networkCapability, 1477 @NonNull DataSetupRetryRule dataRetryRule, @TransportType int transport) { 1478 int count = 0; 1479 for (int i = mDataRetryEntries.size() - 1; i >= 0; i--) { 1480 if (mDataRetryEntries.get(i) instanceof DataSetupRetryEntry) { 1481 DataSetupRetryEntry entry = (DataSetupRetryEntry) mDataRetryEntries.get(i); 1482 // count towards the last succeeded data setup. 1483 if (entry.setupRetryType == DataSetupRetryEntry.RETRY_TYPE_NETWORK_REQUESTS 1484 && entry.transport == transport) { 1485 if (entry.networkRequestList.isEmpty()) { 1486 String msg = "Invalid data retry entry detected"; 1487 logl(msg); 1488 loge("mDataRetryEntries=" + mDataRetryEntries); 1489 AnomalyReporter.reportAnomaly( 1490 UUID.fromString("afeab78c-c0b0-49fc-a51f-f766814d7aa6"), 1491 msg, 1492 mPhone.getCarrierId()); 1493 continue; 1494 } 1495 if (entry.networkRequestList.get(0).getApnTypeNetworkCapability() 1496 == networkCapability 1497 && entry.appliedDataRetryRule.equals(dataRetryRule)) { 1498 if (entry.getState() == DataRetryEntry.RETRY_STATE_SUCCEEDED 1499 || entry.getState() == DataRetryEntry.RETRY_STATE_CANCELLED) { 1500 break; 1501 } 1502 count++; 1503 } 1504 } 1505 } 1506 } 1507 return count; 1508 } 1509 1510 /** 1511 * Schedule the data retry. 1512 * 1513 * @param dataRetryEntry The data retry entry. 1514 */ 1515 private void schedule(@NonNull DataRetryEntry dataRetryEntry) { 1516 logl("Scheduled data retry " + dataRetryEntry + " hashcode=" + dataRetryEntry.hashCode()); 1517 mDataRetryEntries.add(dataRetryEntry); 1518 if (mDataRetryEntries.size() >= MAXIMUM_HISTORICAL_ENTRIES) { 1519 // Discard the oldest retry entry. 1520 mDataRetryEntries.remove(0); 1521 } 1522 1523 // When the device is in doze mode, the handler message might be extremely delayed because 1524 // handler uses relative system time(not counting sleep) which is inaccurate even when we 1525 // enter the maintenance window. 1526 // Therefore, we use alarm manager when we need to schedule long timers. 1527 if (dataRetryEntry.retryDelayMillis <= RETRY_LONG_DELAY_TIMER_THRESHOLD_MILLIS) { 1528 sendMessageDelayed(obtainMessage(dataRetryEntry instanceof DataSetupRetryEntry 1529 ? EVENT_DATA_SETUP_RETRY : EVENT_DATA_HANDOVER_RETRY, dataRetryEntry), 1530 dataRetryEntry.retryDelayMillis); 1531 } else { 1532 if (mFlags.useAlarmCallback()) { 1533 // No need to wake up the device, the retry can wait util next time the device wake 1534 // up to save power. 1535 mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME, 1536 dataRetryEntry.retryElapsedTime, 1537 "dataRetryHash-" + dataRetryEntry.hashCode() /*debug tag*/, 1538 Runnable::run, 1539 null /*worksource*/, 1540 () -> { 1541 logl("onAlarm retry " + dataRetryEntry); 1542 sendMessage(obtainMessage(dataRetryEntry instanceof DataSetupRetryEntry 1543 ? EVENT_DATA_SETUP_RETRY : EVENT_DATA_HANDOVER_RETRY, 1544 dataRetryEntry)); 1545 }); 1546 } else { 1547 Intent intent = new Intent(ACTION_RETRY); 1548 intent.putExtra(ACTION_RETRY_EXTRA_HASHCODE, dataRetryEntry.hashCode()); 1549 // No need to wake up the device, the retry can wait util next time the device wake 1550 // up to save power. 1551 mAlarmManager.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME, 1552 dataRetryEntry.retryElapsedTime, 1553 PendingIntent.getBroadcast(mPhone.getContext(), 1554 dataRetryEntry.hashCode()/*Unique identifier of the retry attempt*/, 1555 intent, 1556 PendingIntent.FLAG_IMMUTABLE)); 1557 } 1558 } 1559 } 1560 1561 /** 1562 * Called when it's time to retry scheduled by Alarm Manager. 1563 * @param retryHashcode The hashcode is the unique identifier of which retry entry to retry. 1564 */ 1565 private void onAlarmIntentRetry(int retryHashcode) { 1566 DataRetryEntry dataRetryEntry = mDataRetryEntries.stream() 1567 .filter(entry -> entry.hashCode() == retryHashcode) 1568 .findAny() 1569 .orElse(null); 1570 logl("onAlarmIntentRetry: found " + dataRetryEntry + " with hashcode " + retryHashcode); 1571 if (dataRetryEntry != null) { 1572 sendMessage(obtainMessage(dataRetryEntry instanceof DataSetupRetryEntry 1573 ? EVENT_DATA_SETUP_RETRY : EVENT_DATA_HANDOVER_RETRY, dataRetryEntry)); 1574 } 1575 } 1576 1577 /** 1578 * Add the latest throttling request and report it to the clients. 1579 * 1580 * @param dataProfile The data profile that is being throttled for setup/handover retry. 1581 * @param networkRequestList The associated network request list when throttling happened. 1582 * Can be {@code null} when retry type is {@link ThrottleStatus#RETRY_TYPE_HANDOVER}. 1583 * @param dataNetwork The data network that is being throttled for handover retry. 1584 * Must be {@code null} when retryType is 1585 * {@link ThrottleStatus#RETRY_TYPE_NEW_CONNECTION}. 1586 * @param retryType The retry type when throttling expires. 1587 * @param transport The transport that the data profile has been throttled on. 1588 * @param expirationTime The expiration time of data throttling. This is the time retrieved from 1589 * {@link SystemClock#elapsedRealtime()}. 1590 */ 1591 private void throttleDataProfile(@NonNull DataProfile dataProfile, 1592 @Nullable NetworkRequestList networkRequestList, 1593 @Nullable DataNetwork dataNetwork, @RetryType int retryType, 1594 @TransportType int transport, @ElapsedRealtimeLong long expirationTime) { 1595 DataThrottlingEntry entry = new DataThrottlingEntry(dataProfile, networkRequestList, 1596 dataNetwork, transport, retryType, expirationTime); 1597 // Remove previous entry that contains the same data profile. Therefore it should always 1598 // contain at maximu all the distinct data profiles of the current subscription times each 1599 // transport. 1600 mDataThrottlingEntries.removeIf( 1601 throttlingEntry -> dataProfile.equals(throttlingEntry.dataProfile) 1602 && (!mFlags.unthrottleCheckTransport() 1603 || throttlingEntry.transport == transport)); 1604 1605 if (mDataThrottlingEntries.size() >= MAXIMUM_HISTORICAL_ENTRIES) { 1606 // If we don't see the anomaly report after U release, we should remove this check for 1607 // the commented reason above. 1608 AnomalyReporter.reportAnomaly( 1609 UUID.fromString("24fd4d46-1d0f-4b13-b7d6-7bad70b8289b"), 1610 "DataRetryManager throttling more than 100 data profiles", 1611 mPhone.getCarrierId()); 1612 mDataThrottlingEntries.remove(0); 1613 } 1614 logl("Add throttling entry " + entry); 1615 mDataThrottlingEntries.add(entry); 1616 1617 // For backwards compatibility, we use RETRY_TYPE_NONE if network suggests never retry. 1618 final int dataRetryType = expirationTime == Long.MAX_VALUE 1619 ? ThrottleStatus.RETRY_TYPE_NONE : retryType; 1620 1621 // Report to the clients. 1622 notifyThrottleStatus(dataProfile, expirationTime, dataRetryType, transport); 1623 } 1624 1625 /** 1626 * Called when network/modem informed to cancelling the previous throttling request. 1627 * 1628 * @param dataProfile The data profile to be unthrottled. Note this is only supported on HAL 1629 * with AIDL interface. When this is set, {@code apn} must be {@code null}. 1630 * @param apn The apn to be unthrottled. Note this should be only used for HIDL 1.6 or below. 1631 * When this is set, {@code dataProfile} must be {@code null}. 1632 * @param transport The transport that this unthrottling request is on. 1633 * @param remove Whether to remove unthrottled entries from the list of entries. 1634 * @param retry Whether schedule retry after unthrottling. 1635 */ 1636 private void onDataProfileUnthrottled(@Nullable DataProfile dataProfile, @Nullable String apn, 1637 @TransportType int transport, boolean remove, boolean retry) { 1638 log("onDataProfileUnthrottled: dataProfile=" + dataProfile + ", apn=" + apn 1639 + ", transport=" + AccessNetworkConstants.transportTypeToString(transport) 1640 + ", remove=" + remove); 1641 1642 long now = SystemClock.elapsedRealtime(); 1643 List<DataThrottlingEntry> dataUnthrottlingEntries = new ArrayList<>(); 1644 if (dataProfile != null) { 1645 // For AIDL-based HAL. There should be only one entry containing this data profile. 1646 // Note that the data profile reconstructed from DataProfileInfo.aidl will not be 1647 // equal to the data profiles kept in data profile manager (due to some fields missing 1648 // in DataProfileInfo.aidl), so we need to get the equivalent data profile from data 1649 // profile manager. 1650 Stream<DataThrottlingEntry> stream = mDataThrottlingEntries.stream(); 1651 stream = stream.filter(entry -> entry.expirationTimeMillis > now 1652 && (!mFlags.unthrottleCheckTransport() || entry.transport == transport)); 1653 if (dataProfile.getApnSetting() != null) { 1654 stream = stream 1655 .filter(entry -> entry.dataProfile.getApnSetting() != null) 1656 .filter(entry -> entry.dataProfile.getApnSetting().getApnName() 1657 .equals(dataProfile.getApnSetting().getApnName())); 1658 } 1659 stream = stream.filter(entry -> Objects.equals(entry.dataProfile.getTrafficDescriptor(), 1660 dataProfile.getTrafficDescriptor())); 1661 1662 dataUnthrottlingEntries = stream.collect(Collectors.toList()); 1663 } else if (apn != null) { 1664 // For HIDL 1.6 or below 1665 dataUnthrottlingEntries = mDataThrottlingEntries.stream() 1666 .filter(entry -> entry.expirationTimeMillis > now 1667 && entry.dataProfile.getApnSetting() != null 1668 && apn.equals(entry.dataProfile.getApnSetting().getApnName()) 1669 && entry.transport == transport) 1670 .collect(Collectors.toList()); 1671 } 1672 1673 if (dataUnthrottlingEntries.isEmpty()) { 1674 log("onDataProfileUnthrottled: Nothing to unthrottle."); 1675 return; 1676 } 1677 1678 // Report to the clients. 1679 final List<ThrottleStatus> throttleStatusList = new ArrayList<>(); 1680 DataProfile unthrottledProfile = null; 1681 int retryType = ThrottleStatus.RETRY_TYPE_NONE; 1682 if (dataUnthrottlingEntries.get(0).retryType == ThrottleStatus.RETRY_TYPE_NEW_CONNECTION) { 1683 unthrottledProfile = dataUnthrottlingEntries.get(0).dataProfile; 1684 retryType = ThrottleStatus.RETRY_TYPE_NEW_CONNECTION; 1685 } else if (dataUnthrottlingEntries.get(0).retryType == ThrottleStatus.RETRY_TYPE_HANDOVER) { 1686 unthrottledProfile = dataUnthrottlingEntries.get(0).dataNetwork.getDataProfile(); 1687 retryType = ThrottleStatus.RETRY_TYPE_HANDOVER; 1688 } 1689 1690 // Make it final so it can be used in the lambda function below. 1691 final int dataRetryType = retryType; 1692 1693 if (unthrottledProfile != null) { 1694 notifyThrottleStatus(unthrottledProfile, ThrottleStatus.Builder.NO_THROTTLE_EXPIRY_TIME, 1695 dataRetryType, transport); 1696 // cancel pending retries since we will soon schedule an immediate retry 1697 cancelRetriesForDataProfile(unthrottledProfile, transport); 1698 } 1699 1700 logl("onDataProfileUnthrottled: Removing the following throttling entries. " 1701 + dataUnthrottlingEntries); 1702 if (retry) { 1703 for (DataThrottlingEntry entry : dataUnthrottlingEntries) { 1704 // Immediately retry after unthrottling. 1705 if (entry.retryType == ThrottleStatus.RETRY_TYPE_NEW_CONNECTION) { 1706 schedule(new DataSetupRetryEntry.Builder<>() 1707 .setDataProfile(entry.dataProfile) 1708 .setTransport(entry.transport) 1709 .setSetupRetryType(DataSetupRetryEntry.RETRY_TYPE_DATA_PROFILE) 1710 .setNetworkRequestList(entry.networkRequestList) 1711 .setRetryDelay(0) 1712 .build()); 1713 } else if (entry.retryType == ThrottleStatus.RETRY_TYPE_HANDOVER) { 1714 schedule(new DataHandoverRetryEntry.Builder<>() 1715 .setDataNetwork(entry.dataNetwork) 1716 .setRetryDelay(0) 1717 .build()); 1718 } 1719 } 1720 } 1721 if (remove) { 1722 mDataThrottlingEntries.removeAll(dataUnthrottlingEntries); 1723 } 1724 } 1725 1726 /** 1727 * Cancel pending retries that uses the specified data profile, with specified target transport. 1728 * 1729 * @param dataProfile The data profile to cancel. 1730 * @param transport The target {@link TransportType} on which the retry to cancel. 1731 */ 1732 private void cancelRetriesForDataProfile(@NonNull DataProfile dataProfile, 1733 @TransportType int transport) { 1734 logl("cancelRetriesForDataProfile: Canceling pending retries for " + dataProfile); 1735 mDataRetryEntries.stream() 1736 .filter(entry -> { 1737 if (entry.getState() == DataRetryEntry.RETRY_STATE_NOT_RETRIED) { 1738 if (entry instanceof DataSetupRetryEntry) { 1739 DataSetupRetryEntry retryEntry = (DataSetupRetryEntry) entry; 1740 return dataProfile.equals(retryEntry.dataProfile) 1741 && transport == retryEntry.transport; 1742 } else if (entry instanceof DataHandoverRetryEntry) { 1743 DataHandoverRetryEntry retryEntry = (DataHandoverRetryEntry) entry; 1744 return dataProfile.equals(retryEntry.dataNetwork.getDataProfile()); 1745 } 1746 } 1747 return false; 1748 }) 1749 .forEach(entry -> entry.setState(DataRetryEntry.RETRY_STATE_CANCELLED)); 1750 } 1751 1752 1753 1754 /** 1755 * Check if there is any similar network request scheduled to retry. The definition of similar 1756 * is that network requests have same APN capability and on the same transport. 1757 * 1758 * @param networkRequest The network request to check. 1759 * @param transport The transport that this request is on. 1760 * @return {@code true} if similar network request scheduled to retry. 1761 */ 1762 public boolean isSimilarNetworkRequestRetryScheduled( 1763 @NonNull TelephonyNetworkRequest networkRequest, @TransportType int transport) { 1764 long now = SystemClock.elapsedRealtime(); 1765 for (int i = mDataRetryEntries.size() - 1; i >= 0; i--) { 1766 if (mDataRetryEntries.get(i) instanceof DataSetupRetryEntry) { 1767 DataSetupRetryEntry entry = (DataSetupRetryEntry) mDataRetryEntries.get(i); 1768 if (entry.getState() == DataRetryEntry.RETRY_STATE_NOT_RETRIED 1769 && entry.setupRetryType 1770 == DataSetupRetryEntry.RETRY_TYPE_NETWORK_REQUESTS 1771 && entry.retryElapsedTime > now) { 1772 if (entry.networkRequestList.isEmpty()) { 1773 String msg = "Invalid data retry entry detected"; 1774 logl(msg); 1775 loge("mDataRetryEntries=" + mDataRetryEntries); 1776 AnomalyReporter.reportAnomaly( 1777 UUID.fromString("781af571-f55d-476d-b510-7a5381f633dc"), 1778 msg, 1779 mPhone.getCarrierId()); 1780 continue; 1781 } 1782 if (entry.networkRequestList.get(0).getApnTypeNetworkCapability() 1783 == networkRequest.getApnTypeNetworkCapability() 1784 && entry.transport == transport) { 1785 return true; 1786 } 1787 } 1788 } 1789 } 1790 return false; 1791 } 1792 1793 /** 1794 * Check if a specific data profile is explicitly throttled by the network. 1795 * 1796 * @param dataProfile The data profile to check. 1797 * @param transport The transport that the request is on. 1798 * @return {@code true} if the data profile is currently throttled. 1799 */ 1800 public boolean isDataProfileThrottled(@NonNull DataProfile dataProfile, 1801 @TransportType int transport) { 1802 long now = SystemClock.elapsedRealtime(); 1803 return mDataThrottlingEntries.stream().anyMatch( 1804 entry -> entry.dataProfile.equals(dataProfile) && entry.expirationTimeMillis > now 1805 && entry.transport == transport); 1806 } 1807 1808 /** 1809 * Cancel pending scheduled handover retry entries. 1810 * 1811 * @param dataNetwork The data network that was originally scheduled for handover retry. 1812 */ 1813 public void cancelPendingHandoverRetry(@NonNull DataNetwork dataNetwork) { 1814 sendMessage(obtainMessage(EVENT_CANCEL_PENDING_HANDOVER_RETRY, dataNetwork)); 1815 } 1816 1817 /** 1818 * Called when cancelling pending scheduled handover retry entries. 1819 * 1820 * @param dataNetwork The data network that was originally scheduled for handover retry. 1821 */ 1822 private void onCancelPendingHandoverRetry(@NonNull DataNetwork dataNetwork) { 1823 mDataRetryEntries.stream() 1824 .filter(entry -> entry instanceof DataHandoverRetryEntry 1825 && ((DataHandoverRetryEntry) entry).dataNetwork == dataNetwork 1826 && entry.getState() == DataRetryEntry.RETRY_STATE_NOT_RETRIED) 1827 .forEach(entry -> entry.setState(DataRetryEntry.RETRY_STATE_CANCELLED)); 1828 1829 long now = SystemClock.elapsedRealtime(); 1830 DataThrottlingEntry dataUnThrottlingEntry = mDataThrottlingEntries.stream() 1831 .filter(entry -> dataNetwork == entry.dataNetwork 1832 && entry.expirationTimeMillis > now).findAny().orElse(null); 1833 if (dataUnThrottlingEntry == null) { 1834 return; 1835 } 1836 log("onCancelPendingHandoverRetry removed throttling entry:" + dataUnThrottlingEntry); 1837 DataProfile unThrottledProfile = 1838 dataUnThrottlingEntry.dataNetwork.getDataProfile(); 1839 final int transport = dataUnThrottlingEntry.transport; 1840 1841 notifyThrottleStatus(unThrottledProfile, ThrottleStatus.Builder.NO_THROTTLE_EXPIRY_TIME, 1842 ThrottleStatus.RETRY_TYPE_HANDOVER, transport); 1843 mDataThrottlingEntries.removeIf(entry -> dataNetwork == entry.dataNetwork); 1844 } 1845 1846 /** 1847 * Notify listeners of throttle status for a given data profile 1848 * 1849 * @param dataProfile Data profile for this throttling status notification 1850 * @param expirationTime Expiration time of throttling status. {@link 1851 * ThrottleStatus.Builder#NO_THROTTLE_EXPIRY_TIME} indicates un-throttling. 1852 * @param dataRetryType Retry type of this throttling notification. 1853 * @param transportType Transport type of this throttling notification. 1854 */ 1855 private void notifyThrottleStatus( 1856 @NonNull DataProfile dataProfile, long expirationTime, @RetryType int dataRetryType, 1857 @TransportType int transportType) { 1858 if (dataProfile.getApnSetting() != null) { 1859 final boolean unThrottled = 1860 expirationTime == ThrottleStatus.Builder.NO_THROTTLE_EXPIRY_TIME; 1861 if (unThrottled) { 1862 dataProfile.getApnSetting().setPermanentFailed(false); 1863 } 1864 final List<ThrottleStatus> throttleStatusList = new ArrayList<>( 1865 dataProfile.getApnSetting().getApnTypes().stream() 1866 .map(apnType -> { 1867 ThrottleStatus.Builder builder = new ThrottleStatus.Builder() 1868 .setApnType(apnType) 1869 .setSlotIndex(mPhone.getPhoneId()) 1870 .setRetryType(dataRetryType) 1871 .setTransportType(transportType); 1872 if (unThrottled) { 1873 builder.setNoThrottle(); 1874 } else { 1875 builder.setThrottleExpiryTimeMillis(expirationTime); 1876 } 1877 return builder.build(); 1878 }).toList()); 1879 mDataRetryManagerCallbacks.forEach(callback -> callback.invokeFromExecutor( 1880 () -> callback.onThrottleStatusChanged(throttleStatusList))); 1881 } 1882 } 1883 1884 /** 1885 * Check if there is any data handover retry scheduled. 1886 * 1887 * @param dataNetwork The network network to retry handover. 1888 * @return {@code true} if there is retry scheduled for this network capability. 1889 */ 1890 public boolean isAnyHandoverRetryScheduled(@NonNull DataNetwork dataNetwork) { 1891 return mDataRetryEntries.stream() 1892 .filter(DataHandoverRetryEntry.class::isInstance) 1893 .map(DataHandoverRetryEntry.class::cast) 1894 .anyMatch(entry -> entry.getState() == DataRetryEntry.RETRY_STATE_NOT_RETRIED 1895 && entry.dataNetwork == dataNetwork); 1896 } 1897 1898 /** 1899 * Register the callback for receiving information from {@link DataRetryManager}. 1900 * 1901 * @param callback The callback. 1902 */ 1903 public void registerCallback(@NonNull DataRetryManagerCallback callback) { 1904 mDataRetryManagerCallbacks.add(callback); 1905 } 1906 1907 /** 1908 * Unregister the previously registered {@link DataRetryManagerCallback}. 1909 * 1910 * @param callback The callback to unregister. 1911 */ 1912 public void unregisterCallback(@NonNull DataRetryManagerCallback callback) { 1913 mDataRetryManagerCallbacks.remove(callback); 1914 } 1915 1916 /** 1917 * Convert reset reason to string 1918 * 1919 * @param reason The reason 1920 * @return The reason in string format. 1921 */ 1922 @NonNull 1923 private static String resetReasonToString(int reason) { 1924 return switch (reason) { 1925 case RESET_REASON_DATA_PROFILES_CHANGED -> "DATA_PROFILES_CHANGED"; 1926 case RESET_REASON_RADIO_ON -> "RADIO_ON"; 1927 case RESET_REASON_MODEM_RESTART -> "MODEM_RESTART"; 1928 case RESET_REASON_DATA_SERVICE_BOUND -> "DATA_SERVICE_BOUND"; 1929 case RESET_REASON_DATA_CONFIG_CHANGED -> "DATA_CONFIG_CHANGED"; 1930 case RESET_REASON_TAC_CHANGED -> "TAC_CHANGED"; 1931 default -> "UNKNOWN(" + reason + ")"; 1932 }; 1933 } 1934 1935 /** 1936 * Log debug messages. 1937 * @param s debug messages 1938 */ 1939 private void log(@NonNull String s) { 1940 Rlog.d(mLogTag, s); 1941 } 1942 1943 /** 1944 * Log error messages. 1945 * @param s error messages 1946 */ 1947 private void loge(@NonNull String s) { 1948 Rlog.e(mLogTag, s); 1949 } 1950 1951 /** 1952 * Log verbose messages. 1953 * @param s debug messages. 1954 */ 1955 private void logv(@NonNull String s) { 1956 if (VDBG) Rlog.v(mLogTag, s); 1957 } 1958 1959 /** 1960 * Log debug messages and also log into the local log. 1961 * @param s debug messages 1962 */ 1963 private void logl(@NonNull String s) { 1964 log(s); 1965 mLocalLog.log(s); 1966 } 1967 1968 /** 1969 * Dump the state of DataRetryManager. 1970 * 1971 * @param fd File descriptor 1972 * @param printWriter Print writer 1973 * @param args Arguments 1974 */ 1975 public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) { 1976 IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); 1977 pw.println(DataRetryManager.class.getSimpleName() + "-" + mPhone.getPhoneId() + ":"); 1978 pw.increaseIndent(); 1979 pw.println("Data Setup Retry rules:"); 1980 pw.increaseIndent(); 1981 mDataSetupRetryRuleList.forEach(pw::println); 1982 pw.decreaseIndent(); 1983 pw.println("Data Handover Retry rules:"); 1984 pw.increaseIndent(); 1985 mDataHandoverRetryRuleList.forEach(pw::println); 1986 pw.decreaseIndent(); 1987 1988 pw.println("Retry entries:"); 1989 pw.increaseIndent(); 1990 for (DataRetryEntry entry : mDataRetryEntries) { 1991 pw.println(entry); 1992 } 1993 pw.decreaseIndent(); 1994 1995 pw.println("Throttling entries:"); 1996 pw.increaseIndent(); 1997 for (DataThrottlingEntry entry : mDataThrottlingEntries) { 1998 pw.println(entry); 1999 } 2000 pw.decreaseIndent(); 2001 2002 pw.println("Local logs:"); 2003 pw.increaseIndent(); 2004 mLocalLog.dump(fd, pw, args); 2005 pw.decreaseIndent(); 2006 pw.decreaseIndent(); 2007 } 2008 } 2009