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