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