1 /*
2  * Copyright 2020 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.google.android.iwlan;
18 
19 import android.content.Context;
20 import android.net.ipsec.ike.exceptions.IkeProtocolException;
21 import android.os.Handler;
22 import android.os.HandlerThread;
23 import android.os.Looper;
24 import android.os.Message;
25 import android.support.annotation.IntDef;
26 import android.support.annotation.NonNull;
27 import android.support.annotation.Nullable;
28 import android.telephony.DataFailCause;
29 import android.telephony.TelephonyManager;
30 import android.telephony.data.DataService;
31 import android.text.TextUtils;
32 import android.util.Log;
33 
34 import com.android.internal.annotations.VisibleForTesting;
35 
36 import com.google.auto.value.AutoValue;
37 
38 import org.json.JSONArray;
39 import org.json.JSONException;
40 import org.json.JSONObject;
41 
42 import java.io.PrintWriter;
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.Calendar;
46 import java.util.Date;
47 import java.util.HashMap;
48 import java.util.HashSet;
49 import java.util.List;
50 import java.util.Map;
51 import java.util.Objects;
52 import java.util.Optional;
53 import java.util.Set;
54 import java.util.concurrent.ConcurrentHashMap;
55 import java.util.concurrent.TimeUnit;
56 
57 public class ErrorPolicyManager {
58 
59     /**
60      * This type is not to be used in config. This is only used internally to catch errors in
61      * parsing the error type.
62      */
63     private static final int UNKNOWN_ERROR_TYPE = -1;
64 
65     /**
66      * This value represents that the error tye is to be used as a fallback to represent all the
67      * errors.
68      */
69     private static final int FALLBACK_ERROR_TYPE = 1;
70 
71     /**
72      * This value represents rest of the errors that are not defined above. ErrorDetails should
73      * mention the specific error. If it doesn't - the policy will be used as a fallback global
74      * policy. Currently, Supported ErrorDetails "IO_EXCEPTION" "TIMEOUT_EXCEPTION"
75      * "SERVER_SELECTION_FAILED" "TUNNEL_TRANSFORM_FAILED"
76      */
77     private static final int GENERIC_ERROR_TYPE = 2;
78 
79     /**
80      * This value represents IKE Protocol Error/Notify Error.
81      *
82      * @see <a href="https://tools.ietf.org/html/rfc4306#section-3.10.1">RFC 4306,Internet Key
83      *     Exchange (IKEv2) Protocol </a> for global errors and carrier specific requirements for
84      *     other carrier specific error codes. ErrorDetails defined for this type is always in
85      *     numeric form representing the error codes. Examples: "24", "9000-9050"
86      */
87     private static final int IKE_PROTOCOL_ERROR_TYPE = 3;
88 
builder()89     static ErrorPolicy.Builder builder() {
90         return new AutoValue_ErrorPolicyManager_ErrorPolicy.Builder()
91                 .setInfiniteRetriesWithLastRetryTime(false);
92     }
93 
94     @IntDef({UNKNOWN_ERROR_TYPE, FALLBACK_ERROR_TYPE, GENERIC_ERROR_TYPE, IKE_PROTOCOL_ERROR_TYPE})
95     @interface ErrorPolicyErrorType {}
96 
97     private static final String[] GENERIC_ERROR_DETAIL_STRINGS = {
98         "*",
99         "IO_EXCEPTION",
100         "TIMEOUT_EXCEPTION",
101         "SERVER_SELECTION_FAILED",
102         "TUNNEL_TRANSFORM_FAILED"
103     };
104 
105     /** Private IKEv2 notify message types. As defined in TS 124 302 (section 8.1.2.2) */
106     private static final int IKE_PROTOCOL_ERROR_PDN_CONNECTION_REJECTION = 8192;
107 
108     private static final int IKE_PROTOCOL_ERROR_MAX_CONNECTION_REACHED = 8193;
109     private static final int IKE_PROTOCOL_ERROR_SEMANTIC_ERROR_IN_THE_TFT_OPERATION = 8241;
110     private static final int IKE_PROTOCOL_ERROR_SYNTACTICAL_ERROR_IN_THE_TFT_OPERATION = 8242;
111     private static final int IKE_PROTOCOL_ERROR_SEMANTIC_ERRORS_IN_PACKET_FILTERS = 8244;
112     private static final int IKE_PROTOCOL_ERROR_SYNTACTICAL_ERRORS_IN_PACKET_FILTERS = 8245;
113     private static final int IKE_PROTOCOL_ERROR_NON_3GPP_ACCESS_TO_EPC_NOT_ALLOWED = 9000;
114     private static final int IKE_PROTOCOL_ERROR_USER_UNKNOWN = 9001;
115     private static final int IKE_PROTOCOL_ERROR_NO_APN_SUBSCRIPTION = 9002;
116     private static final int IKE_PROTOCOL_ERROR_AUTHORIZATION_REJECTED = 9003;
117     private static final int IKE_PROTOCOL_ERROR_ILLEGAL_ME = 9006;
118     private static final int IKE_PROTOCOL_ERROR_NETWORK_FAILURE = 10500;
119     private static final int IKE_PROTOCOL_ERROR_RAT_TYPE_NOT_ALLOWED = 11001;
120     private static final int IKE_PROTOCOL_ERROR_IMEI_NOT_ACCEPTED = 11005;
121     private static final int IKE_PROTOCOL_ERROR_PLMN_NOT_ALLOWED = 11011;
122     private static final int IKE_PROTOCOL_ERROR_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED = 11055;
123 
124     /** Private IKEv2 notify message types, as defined in TS 124 502 (section 9.2.4.1) */
125     private static final int IKE_PROTOCOL_ERROR_CONGESTION = 15500;
126 
127     private static final int IWLAN_NO_ERROR_RETRY_TIME = -1;
128 
129     private static final ErrorPolicy FALLBACK_ERROR_POLICY =
130             builder()
131                     .setErrorType(FALLBACK_ERROR_TYPE)
132                     .setRetryArray(List.of(5, -1))
133                     .setErrorDetails(List.of("*"))
134                     .setUnthrottlingEvents(List.of())
135                     .build();
136 
137     private final String LOG_TAG;
138 
139     private static final Map<Integer, ErrorPolicyManager> mInstances = new ConcurrentHashMap<>();
140     private final Context mContext;
141     private final int mSlotId;
142 
143     // Policies read from defaultiwlanerrorconfig.json
144     // String APN as key to identify the ErrorPolicies associated with it.
145     private final Map<String, List<ErrorPolicy>> mDefaultPolicies = new HashMap<>();
146 
147     // Policies read from CarrierConfig
148     // String APN as key to identify the ErrorPolicies associated with it.
149     private final Map<String, List<ErrorPolicy>> mCarrierConfigPolicies = new HashMap<>();
150 
151     /** String APN as key to identify the {@link ApnRetryActionStore} associated with that APN */
152     private final Map<String, ApnRetryActionStore> mRetryActionStoreByApn =
153             new ConcurrentHashMap<>();
154 
155     // Records the most recently reported IwlanError (including NO_ERROR), and the corresponding
156     // APN.
157     private ApnWithIwlanError mMostRecentError;
158 
159     // List of current Unthrottling events registered with IwlanEventListener
160     private Set<Integer> mUnthrottlingEvents;
161 
162     private final ErrorStats mErrorStats = new ErrorStats();
163 
164     private HandlerThread mHandlerThread;
165     @VisibleForTesting Handler mHandler;
166 
167     private int carrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
168 
169     private String mCarrierConfigErrorPolicyString;
170 
171     /**
172      * Returns ErrorPolicyManager instance for the subId
173      *
174      * @param context
175      * @param slotId
176      */
getInstance(@onNull Context context, int slotId)177     public static ErrorPolicyManager getInstance(@NonNull Context context, int slotId) {
178         return mInstances.computeIfAbsent(slotId, k -> new ErrorPolicyManager(context, slotId));
179     }
180 
181     @VisibleForTesting
resetAllInstances()182     public static void resetAllInstances() {
183         mInstances.clear();
184     }
185 
186     /**
187      * Release or reset the instance.
188      */
releaseInstance()189     public void releaseInstance() {
190         Log.d(LOG_TAG, "Release Instance with slotId: " + mSlotId);
191         IwlanEventListener.getInstance(mContext, mSlotId).removeEventListener(mHandler);
192         mHandlerThread.quit();
193         mInstances.remove(mSlotId);
194     }
195 
196     /**
197      * Updates the last error details and returns the retry time. Return value is -1, which should
198      * be ignored, when the error is IwlanError.NO_ERROR.
199      *
200      * @param apn apn name for which the error happened
201      * @param iwlanError Error
202      * @return retry time. 0 = immediate retry, -1 = fail and n = retry after n seconds
203      */
reportIwlanError(String apn, IwlanError iwlanError)204     public synchronized long reportIwlanError(String apn, IwlanError iwlanError) {
205         // Fail by default
206         mMostRecentError = new ApnWithIwlanError(apn, iwlanError);
207 
208         if (iwlanError.getErrorType() == IwlanError.NO_ERROR) {
209             Log.d(LOG_TAG, "reportIwlanError: NO_ERROR");
210             mRetryActionStoreByApn.remove(apn);
211             return IWLAN_NO_ERROR_RETRY_TIME;
212         }
213         mErrorStats.update(apn, iwlanError);
214 
215         PolicyDerivedRetryAction newRetryAction =
216                 mRetryActionStoreByApn
217                         .computeIfAbsent(apn, ApnRetryActionStore::new)
218                         .generateRetryAction(iwlanError);
219 
220         Log.d(
221                 LOG_TAG,
222                 "Current RetryAction index: "
223                         + newRetryAction.currentRetryIndex()
224                         + " and time: "
225                         + newRetryAction.totalRetryTimeMs());
226         return newRetryAction.totalRetryTimeMs() / 1000;
227     }
228 
229     /**
230      * Updates the last error details with backoff time.
231      *
232      * @param apn apn name for which the error happened
233      * @param iwlanError Error
234      * @param backoffTime in seconds
235      * @return retry time which is the backoff time. -1 if it is {@link IwlanError#NO_ERROR}
236      */
reportIwlanError(String apn, IwlanError iwlanError, long backoffTime)237     public synchronized long reportIwlanError(String apn, IwlanError iwlanError, long backoffTime) {
238         // Fail by default
239         if (iwlanError.getErrorType() == IwlanError.NO_ERROR) {
240             Log.d(LOG_TAG, "reportIwlanError: NO_ERROR");
241             mRetryActionStoreByApn.remove(apn);
242             return IWLAN_NO_ERROR_RETRY_TIME;
243         }
244         mErrorStats.update(apn, iwlanError);
245 
246         IkeBackoffNotifyRetryAction newRetryAction =
247                 mRetryActionStoreByApn
248                         .computeIfAbsent(apn, ApnRetryActionStore::new)
249                         .generateRetryAction(iwlanError, backoffTime);
250         Log.d(LOG_TAG, "Current configured backoff time: " + newRetryAction.backoffTime());
251 
252         return newRetryAction.backoffTime();
253     }
254 
255     /**
256      * Checks whether we can bring up Epdg Tunnel - Based on lastErrorForApn
257      *
258      * @param apn apn for which tunnel bring up needs to be checked
259      * @return true if tunnel can be brought up, false otherwise
260      */
canBringUpTunnel(String apn)261     public synchronized boolean canBringUpTunnel(String apn) {
262         RetryAction lastRetryAction = getLastRetryAction(apn);
263         boolean canBringUp =
264                 lastRetryAction == null || getRemainingRetryTimeMs(lastRetryAction) <= 0;
265         Log.d(LOG_TAG, "canBringUpTunnel: " + canBringUp);
266         return canBringUp;
267     }
268 
269     // TODO: Modify framework/base/Android.bp to get access to Annotation.java to use
270     // @DataFailureCause
271     // annotation as return type here. (after moving to aosp?)
272     /**
273      * Returns the DataFailCause based on the lastErrorForApn
274      *
275      * @param apn apn name for which DataFailCause is needed
276      * @return DataFailCause corresponding to the error for the apn
277      */
getDataFailCause(String apn)278     public synchronized int getDataFailCause(String apn) {
279         RetryAction lastRetryAction = getLastRetryAction(apn);
280         return lastRetryAction == null
281                 ? DataFailCause.NONE
282                 : getDataFailCause(lastRetryAction.error());
283     }
284 
getDataFailCause(IwlanError error)285     private int getDataFailCause(IwlanError error) {
286         int ret;
287         int errorType = error.getErrorType();
288         switch (errorType) {
289             case IwlanError.NO_ERROR -> ret = DataFailCause.NONE;
290             case IwlanError.IKE_PROTOCOL_EXCEPTION ->
291                     ret = getDataFailCauseForIkeProtocolException(error.getException());
292             case IwlanError.IKE_INTERNAL_IO_EXCEPTION ->
293                     ret = DataFailCause.IWLAN_IKEV2_MSG_TIMEOUT;
294             case IwlanError.IKE_GENERIC_EXCEPTION -> ret = DataFailCause.ERROR_UNSPECIFIED;
295             case IwlanError.EPDG_SELECTOR_SERVER_SELECTION_FAILED ->
296                     ret = DataFailCause.IWLAN_DNS_RESOLUTION_NAME_FAILURE;
297             case IwlanError.TUNNEL_TRANSFORM_FAILED ->
298                     ret = DataFailCause.IWLAN_TUNNEL_TRANSFORM_FAILED;
299             case IwlanError.SIM_NOT_READY_EXCEPTION -> ret = DataFailCause.SIM_CARD_CHANGED;
300             case IwlanError.IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED ->
301                     ret = DataFailCause.IWLAN_IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED;
302             case IwlanError.IKE_NETWORK_LOST_EXCEPTION ->
303                     ret = DataFailCause.IWLAN_IKE_NETWORK_LOST_EXCEPTION;
304             case IwlanError.TUNNEL_NOT_FOUND -> ret = DataFailCause.IWLAN_TUNNEL_NOT_FOUND;
305             case IwlanError.EPDG_ADDRESS_ONLY_IPV4_ALLOWED -> ret = DataFailCause.ONLY_IPV4_ALLOWED;
306             case IwlanError.EPDG_ADDRESS_ONLY_IPV6_ALLOWED -> ret = DataFailCause.ONLY_IPV6_ALLOWED;
307             case IwlanError.IKE_INIT_TIMEOUT -> ret = DataFailCause.IWLAN_IKE_INIT_TIMEOUT;
308             case IwlanError.IKE_MOBILITY_TIMEOUT -> ret = DataFailCause.IWLAN_IKE_MOBILITY_TIMEOUT;
309             case IwlanError.IKE_DPD_TIMEOUT -> ret = DataFailCause.IWLAN_IKE_DPD_TIMEOUT;
310             default -> ret = DataFailCause.ERROR_UNSPECIFIED;
311         }
312         return ret;
313     }
314 
315     // TODO: create DFC for all IkeProtocolExceptions and assign here.
getDataFailCauseForIkeProtocolException(Exception exception)316     private int getDataFailCauseForIkeProtocolException(Exception exception) {
317         if (!(exception instanceof IkeProtocolException ikeProtocolException)) {
318             return DataFailCause.IWLAN_IKE_PRIVATE_PROTOCOL_ERROR;
319         }
320 
321         int protocolErrorType = ikeProtocolException.getErrorType();
322         switch (protocolErrorType) {
323             case IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED:
324                 return DataFailCause.IWLAN_IKEV2_AUTH_FAILURE;
325             case IkeProtocolException.ERROR_TYPE_INTERNAL_ADDRESS_FAILURE:
326                 return DataFailCause.IWLAN_EPDG_INTERNAL_ADDRESS_FAILURE;
327             case IKE_PROTOCOL_ERROR_PDN_CONNECTION_REJECTION:
328                 return DataFailCause.IWLAN_PDN_CONNECTION_REJECTION;
329             case IKE_PROTOCOL_ERROR_MAX_CONNECTION_REACHED:
330                 return DataFailCause.IWLAN_MAX_CONNECTION_REACHED;
331             case IKE_PROTOCOL_ERROR_SEMANTIC_ERROR_IN_THE_TFT_OPERATION:
332                 return DataFailCause.IWLAN_SEMANTIC_ERROR_IN_THE_TFT_OPERATION;
333             case IKE_PROTOCOL_ERROR_SYNTACTICAL_ERROR_IN_THE_TFT_OPERATION:
334                 return DataFailCause.IWLAN_SYNTACTICAL_ERROR_IN_THE_TFT_OPERATION;
335             case IKE_PROTOCOL_ERROR_SEMANTIC_ERRORS_IN_PACKET_FILTERS:
336                 return DataFailCause.IWLAN_SEMANTIC_ERRORS_IN_PACKET_FILTERS;
337             case IKE_PROTOCOL_ERROR_SYNTACTICAL_ERRORS_IN_PACKET_FILTERS:
338                 return DataFailCause.IWLAN_SYNTACTICAL_ERRORS_IN_PACKET_FILTERS;
339             case IKE_PROTOCOL_ERROR_NON_3GPP_ACCESS_TO_EPC_NOT_ALLOWED:
340                 return DataFailCause.IWLAN_NON_3GPP_ACCESS_TO_EPC_NOT_ALLOWED;
341             case IKE_PROTOCOL_ERROR_USER_UNKNOWN:
342                 return DataFailCause.IWLAN_USER_UNKNOWN;
343             case IKE_PROTOCOL_ERROR_NO_APN_SUBSCRIPTION:
344                 return DataFailCause.IWLAN_NO_APN_SUBSCRIPTION;
345             case IKE_PROTOCOL_ERROR_AUTHORIZATION_REJECTED:
346                 return DataFailCause.IWLAN_AUTHORIZATION_REJECTED;
347             case IKE_PROTOCOL_ERROR_ILLEGAL_ME:
348                 return DataFailCause.IWLAN_ILLEGAL_ME;
349             case IKE_PROTOCOL_ERROR_NETWORK_FAILURE:
350                 return DataFailCause.IWLAN_NETWORK_FAILURE;
351             case IKE_PROTOCOL_ERROR_RAT_TYPE_NOT_ALLOWED:
352                 return DataFailCause.IWLAN_RAT_TYPE_NOT_ALLOWED;
353             case IKE_PROTOCOL_ERROR_IMEI_NOT_ACCEPTED:
354                 return DataFailCause.IWLAN_IMEI_NOT_ACCEPTED;
355             case IKE_PROTOCOL_ERROR_PLMN_NOT_ALLOWED:
356                 return DataFailCause.IWLAN_PLMN_NOT_ALLOWED;
357             case IKE_PROTOCOL_ERROR_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED:
358                 return DataFailCause.IWLAN_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED;
359             case IKE_PROTOCOL_ERROR_CONGESTION:
360                 return DataFailCause.IWLAN_CONGESTION;
361             default:
362                 return DataFailCause.IWLAN_IKE_PRIVATE_PROTOCOL_ERROR;
363         }
364     }
365 
getMostRecentDataFailCause()366     public synchronized int getMostRecentDataFailCause() {
367         if (mMostRecentError != null) {
368             return getDataFailCause(mMostRecentError.mIwlanError);
369         }
370         return DataFailCause.NONE;
371     }
372 
373     /**
374      * Returns the current retryTime based on the lastErrorForApn
375      *
376      * @param apn apn name for which curren retry time is needed
377      * @return long current retry time in milliseconds
378      */
getRemainingRetryTimeMs(String apn)379     public synchronized long getRemainingRetryTimeMs(String apn) {
380         RetryAction lastRetryAction = getLastRetryAction(apn);
381         return lastRetryAction == null ? -1 : getRemainingRetryTimeMs(lastRetryAction);
382     }
383 
384     /**
385      * Get the remaining time in millis should be waited before retry, based on the current time and
386      * the RetryAction.
387      */
getRemainingRetryTimeMs(RetryAction retryAction)388     private static long getRemainingRetryTimeMs(RetryAction retryAction) {
389         long totalRetryTimeMs = retryAction.totalRetryTimeMs();
390         long errorTime = retryAction.lastErrorTime();
391         long currentTime = IwlanHelper.elapsedRealtime();
392         return Math.max(0, totalRetryTimeMs - (currentTime - errorTime));
393     }
394 
395     /**
396      * Gets the last error count of the APN
397      *
398      * @param apn the APN
399      * @return the error count for the last error cause of the APN, 0 if no error or unthrottled
400      */
getLastErrorCountOfSameCause(String apn)401     public synchronized int getLastErrorCountOfSameCause(String apn) {
402         RetryAction retryAction = getLastRetryAction(apn);
403         return retryAction != null ? retryAction.errorCountOfSameCause() : 0;
404     }
405 
406     /**
407      * Returns the index of the FQDN to use for ePDG server selection, based on how many FQDNs are
408      * available, the position of the RetryArray index, and configuration of 'NumAttemptsPerFqdn'.
409      * This method assumes backoff time is not configured.
410      *
411      * @param numFqdns number of FQDNs discovered during ePDG server selection.
412      * @return int index of the FQDN to use for ePDG server selection. -1 (invalid) if RetryArray or
413      *     'NumAttemptsPerFqdn' is not specified in the ErrorPolicy.
414      */
getCurrentFqdnIndex(int numFqdns)415     public synchronized int getCurrentFqdnIndex(int numFqdns) {
416         String apn = mMostRecentError.mApn;
417         RetryAction lastRetryAction = getLastRetryAction(apn);
418         return lastRetryAction == null ? -1 : lastRetryAction.getCurrentFqdnIndex(numFqdns);
419     }
420 
421     @Nullable
getLastRetryAction(String apn)422     private synchronized RetryAction getLastRetryAction(String apn) {
423         ApnRetryActionStore retryActionStore = mRetryActionStoreByApn.get(apn);
424         return retryActionStore == null ? null : retryActionStore.getLastRetryAction();
425     }
426 
427     /**
428      * Returns the last error for that apn
429      *
430      * @param apn apn name
431      * @return IwlanError or null if there is no error
432      */
getLastError(String apn)433     public synchronized IwlanError getLastError(String apn) {
434         RetryAction lastRetryAction = getLastRetryAction(apn);
435         return lastRetryAction == null
436                 ? new IwlanError(IwlanError.NO_ERROR)
437                 : lastRetryAction.error();
438     }
439 
440     /**
441      * Returns whether framework should retry tunnel setup with initial PDN bringup request when
442      * handover request fails.
443      *
444      * @param apn apn name
445      * @return boolean result of whether framework should retry tunnel setup with initial PDN
446      *     bringup request when handover request fails
447      */
shouldRetryWithInitialAttach(String apn)448     public synchronized boolean shouldRetryWithInitialAttach(String apn) {
449         RetryAction retryAction = getLastRetryAction(apn);
450         return retryAction != null && retryAction.shouldRetryWithInitialAttach();
451     }
452 
logErrorPolicies()453     public void logErrorPolicies() {
454         Log.d(LOG_TAG, "mCarrierConfigPolicies:");
455         for (Map.Entry<String, List<ErrorPolicy>> entry : mCarrierConfigPolicies.entrySet()) {
456             Log.d(LOG_TAG, "Apn: " + entry.getKey());
457             for (ErrorPolicy policy : entry.getValue()) {
458                 policy.log();
459             }
460         }
461         Log.d(LOG_TAG, "mDefaultPolicies:");
462         for (Map.Entry<String, List<ErrorPolicy>> entry : mDefaultPolicies.entrySet()) {
463             Log.d(LOG_TAG, "Apn: " + entry.getKey());
464             for (ErrorPolicy policy : entry.getValue()) {
465                 policy.log();
466             }
467         }
468     }
469 
dump(PrintWriter pw)470     public synchronized void dump(PrintWriter pw) {
471         pw.println("---- ErrorPolicyManager ----");
472         mRetryActionStoreByApn.forEach(
473                 (apn, retryActionStore) -> {
474                     pw.println("APN: " + apn);
475                     pw.println("Last RetryAction: " + retryActionStore.getLastRetryAction());
476                     retryActionStore.mLastRetryActionByCause.forEach(
477                             (cause, retryAction) -> {
478                                 pw.println(cause);
479                                 pw.println(retryAction);
480                             });
481                 });
482         pw.println(mErrorStats);
483         pw.println("----------------------------");
484     }
485 
ErrorPolicyManager(Context context, int slotId)486     private ErrorPolicyManager(Context context, int slotId) {
487         mContext = context;
488         mSlotId = slotId;
489         LOG_TAG = ErrorPolicyManager.class.getSimpleName() + "[" + slotId + "]";
490 
491         initHandler();
492 
493         // read from default error policy config
494         try {
495             mDefaultPolicies.putAll(
496                     readErrorPolicies(
497                             new JSONArray(
498                                     IwlanCarrierConfig.getDefaultConfigString(
499                                             IwlanCarrierConfig.KEY_ERROR_POLICY_CONFIG_STRING))));
500         } catch (JSONException | IllegalArgumentException e) {
501             throw new AssertionError(e);
502         }
503 
504         mCarrierConfigErrorPolicyString = null;
505         readFromCarrierConfig(IwlanHelper.getCarrierId(mContext, mSlotId));
506         updateUnthrottlingEvents();
507     }
508 
findErrorPolicy(String apn, IwlanError iwlanError)509     private ErrorPolicy findErrorPolicy(String apn, IwlanError iwlanError) {
510         ErrorPolicy policy = null;
511 
512         if (mCarrierConfigPolicies.containsKey(apn)) {
513             policy = getPreferredErrorPolicy(mCarrierConfigPolicies.get(apn), iwlanError);
514         }
515         if (policy == null && mCarrierConfigPolicies.containsKey("*")) {
516             policy = getPreferredErrorPolicy(mCarrierConfigPolicies.get("*"), iwlanError);
517         }
518         if (policy == null && mDefaultPolicies.containsKey(apn)) {
519             policy = getPreferredErrorPolicy(mDefaultPolicies.get(apn), iwlanError);
520         }
521         if (policy == null && mDefaultPolicies.containsKey("*")) {
522             policy = getPreferredErrorPolicy(mDefaultPolicies.get("*"), iwlanError);
523         }
524 
525         if (policy == null) {
526             // there should at least be one default policy defined in Default config
527             // that will apply to all errors.
528             // should not reach here in any situation, default config should be configured in
529             // defaultiwlanerrorconfig.json. here is just for prevent runtime exception
530             logErrorPolicies();
531             Log.e(LOG_TAG, "No matched error policy");
532             policy = FALLBACK_ERROR_POLICY;
533         }
534         return policy;
535     }
536 
getPreferredErrorPolicy( List<ErrorPolicy> errorPolicies, IwlanError iwlanError)537     private ErrorPolicy getPreferredErrorPolicy(
538             List<ErrorPolicy> errorPolicies, IwlanError iwlanError) {
539 
540         ErrorPolicy selectedPolicy = null;
541         for (ErrorPolicy policy : errorPolicies) {
542             if (policy.match(iwlanError)) {
543                 if (!policy.isFallback()) {
544                     selectedPolicy = policy;
545                     break;
546                 }
547                 if (selectedPolicy == null || policy.getErrorType() != GENERIC_ERROR_TYPE) {
548                     selectedPolicy = policy;
549                 }
550             }
551         }
552         return selectedPolicy;
553     }
554 
555     @VisibleForTesting
initHandler()556     void initHandler() {
557         mHandler = new EpmHandler(getLooper());
558     }
559 
560     @VisibleForTesting
getLooper()561     Looper getLooper() {
562         mHandlerThread = new HandlerThread("ErrorPolicyManagerThread");
563         mHandlerThread.start();
564         return mHandlerThread.getLooper();
565     }
566 
567     @VisibleForTesting
readErrorPolicies(JSONArray apnArray)568     Map<String, List<ErrorPolicy>> readErrorPolicies(JSONArray apnArray)
569             throws JSONException, IllegalArgumentException {
570         Map<String, List<ErrorPolicy>> errorPolicies = new HashMap<>();
571         for (int i = 0; i < apnArray.length(); i++) {
572             JSONObject apnDetails = apnArray.getJSONObject(i);
573 
574             String apnName = ((String) apnDetails.get("ApnName")).trim();
575             JSONArray errorTypeArray = (JSONArray) apnDetails.get("ErrorTypes");
576 
577             for (int j = 0; j < errorTypeArray.length(); j++) {
578                 JSONObject errorTypeObject = errorTypeArray.getJSONObject(j);
579 
580                 String errorTypeStr = ((String) errorTypeObject.get("ErrorType")).trim();
581                 JSONArray errorDetailArray = (JSONArray) errorTypeObject.get("ErrorDetails");
582                 int errorType;
583 
584                 if ((errorType = getErrorPolicyErrorType(errorTypeStr)) == UNKNOWN_ERROR_TYPE) {
585                     throw new IllegalArgumentException("Unknown error type in the parsing");
586                 }
587 
588                 List<Integer> retryArray =
589                         parseRetryArray((JSONArray) errorTypeObject.get("RetryArray"));
590 
591                 ErrorPolicy.Builder errorPolicyBuilder =
592                         builder()
593                                 .setErrorType(errorType)
594                                 .setErrorDetails(parseErrorDetails(errorType, errorDetailArray))
595                                 .setRetryArray(retryArray)
596                                 .setUnthrottlingEvents(
597                                         parseUnthrottlingEvents(
598                                                 (JSONArray)
599                                                         errorTypeObject.get("UnthrottlingEvents")));
600 
601                 if (!retryArray.isEmpty() && retryArray.get(retryArray.size() - 1) == -1L) {
602                     errorPolicyBuilder.setInfiniteRetriesWithLastRetryTime(true);
603                 }
604 
605                 if (errorTypeObject.has("NumAttemptsPerFqdn")) {
606                     errorPolicyBuilder.setNumAttemptsPerFqdn(
607                             errorTypeObject.getInt("NumAttemptsPerFqdn"));
608                 }
609 
610                 if (errorTypeObject.has("HandoverAttemptCount")) {
611                     if (errorType != IKE_PROTOCOL_ERROR_TYPE) {
612                         throw new IllegalArgumentException(
613                                 "Handover attempt count should not be applied when errorType is not"
614                                         + " explicitly defined as IKE_PROTOCOL_ERROR_TYPE");
615                     }
616                     errorPolicyBuilder.setHandoverAttemptCount(
617                             errorTypeObject.getInt("HandoverAttemptCount"));
618                 }
619 
620                 ErrorPolicy errorPolicy = errorPolicyBuilder.build();
621 
622                 errorPolicies.putIfAbsent(apnName, new ArrayList<>());
623                 errorPolicies.get(apnName).add(errorPolicy);
624             }
625         }
626         return errorPolicies;
627     }
628 
parseRetryArray(JSONArray retryArray)629     private List<Integer> parseRetryArray(JSONArray retryArray)
630             throws JSONException, IllegalArgumentException {
631         List<Integer> ret = new ArrayList<>();
632         for (int i = 0; i < retryArray.length(); i++) {
633             String retryTime = retryArray.getString(i).trim();
634 
635             // catch misplaced -1 retry times in the array.
636             // 1. if it is not placed at the last position in the array
637             // 2. if it is placed in the first position (catches the case where it is
638             //    the only element).
639             if (retryTime.equals("-1") && (i != retryArray.length() - 1 || i == 0)) {
640                 throw new IllegalArgumentException("Misplaced -1 in retry array");
641             }
642             if (TextUtils.isDigitsOnly(retryTime) || retryTime.equals("-1")) {
643                 ret.add(Integer.parseInt(retryTime));
644             } else if (retryTime.contains("+r")) {
645                 // randomized retry time
646                 String[] times = retryTime.split("\\+r");
647                 if (times.length == 2
648                         && TextUtils.isDigitsOnly(times[0])
649                         && TextUtils.isDigitsOnly(times[1])) {
650                     ret.add(
651                             Integer.parseInt(times[0])
652                                     + (int) (Math.random() * Long.parseLong(times[1])));
653                 } else {
654                     throw new IllegalArgumentException(
655                             "Randomized Retry time is not in acceptable format");
656                 }
657             } else {
658                 throw new IllegalArgumentException("Retry time is not in acceptable format");
659             }
660         }
661         return ret;
662     }
663 
parseUnthrottlingEvents(JSONArray unthrottlingEvents)664     private List<Integer> parseUnthrottlingEvents(JSONArray unthrottlingEvents)
665             throws JSONException, IllegalArgumentException {
666         List<Integer> ret = new ArrayList<>();
667         for (int i = 0; i < unthrottlingEvents.length(); i++) {
668             int event =
669                     IwlanEventListener.getUnthrottlingEvent(unthrottlingEvents.getString(i).trim());
670             if (event == IwlanEventListener.UNKNOWN_EVENT) {
671                 throw new IllegalArgumentException(
672                         "Unexpected unthrottlingEvent " + unthrottlingEvents.getString(i));
673             }
674             ret.add(event);
675         }
676         return ret;
677     }
678 
parseErrorDetails(int errorType, JSONArray errorDetailArray)679     private List<String> parseErrorDetails(int errorType, JSONArray errorDetailArray)
680             throws JSONException, IllegalArgumentException {
681         List<String> ret = new ArrayList<>();
682         boolean isValidErrorDetail = true;
683 
684         for (int i = 0; i < errorDetailArray.length(); i++) {
685             String errorDetail = errorDetailArray.getString(i).trim();
686             switch (errorType) {
687                 case IKE_PROTOCOL_ERROR_TYPE:
688                     isValidErrorDetail = verifyIkeProtocolErrorDetail(errorDetail);
689                     break;
690                 case GENERIC_ERROR_TYPE:
691                     isValidErrorDetail = verifyGenericErrorDetail(errorDetail);
692                     break;
693             }
694             if (!isValidErrorDetail) {
695                 throw new IllegalArgumentException(
696                         "Invalid ErrorDetail: " + errorDetail + " for ErrorType: " + errorType);
697             }
698             ret.add(errorDetail);
699         }
700         return ret;
701     }
702 
703     /** Allowed formats are: number(Integer), range(Integers separated by -) and "*" */
verifyIkeProtocolErrorDetail(String errorDetailStr)704     private boolean verifyIkeProtocolErrorDetail(String errorDetailStr) {
705         boolean ret = true;
706         if (errorDetailStr.contains("-")) {
707             // verify range format
708             String[] rangeNumbers = errorDetailStr.split("-");
709             if (rangeNumbers.length == 2) {
710                 for (String range : rangeNumbers) {
711                     if (!TextUtils.isDigitsOnly(range)) {
712                         ret = false;
713                     }
714                 }
715             } else {
716                 ret = false;
717             }
718         } else if (!errorDetailStr.equals("*") && !TextUtils.isDigitsOnly(errorDetailStr)) {
719             ret = false;
720         }
721         return ret;
722     }
723 
724     /**
725      * Allowed strings are: "IO_EXCEPTION", "TIMEOUT_EXCEPTION", "SERVER_SELECTION_FAILED",
726      * "TUNNEL_TRANSFORM_FAILED" and "*"
727      */
verifyGenericErrorDetail(String errorDetailStr)728     private boolean verifyGenericErrorDetail(String errorDetailStr) {
729         boolean ret = false;
730         for (String str : GENERIC_ERROR_DETAIL_STRINGS) {
731             if (errorDetailStr.equals(str)) {
732                 ret = true;
733                 break;
734             }
735         }
736         return ret;
737     }
738 
getErrorPolicyErrorType(String errorType)739     private @ErrorPolicyErrorType int getErrorPolicyErrorType(String errorType) {
740         int ret = UNKNOWN_ERROR_TYPE;
741         switch (errorType) {
742             case "IKE_PROTOCOL_ERROR_TYPE":
743                 ret = IKE_PROTOCOL_ERROR_TYPE;
744                 break;
745             case "GENERIC_ERROR_TYPE":
746                 ret = GENERIC_ERROR_TYPE;
747                 break;
748             case "*":
749                 ret = FALLBACK_ERROR_TYPE;
750                 break;
751         }
752         return ret;
753     }
754 
getAllUnthrottlingEvents()755     private synchronized Set<Integer> getAllUnthrottlingEvents() {
756         Set<Integer> events = new HashSet<>();
757         for (Map.Entry<String, List<ErrorPolicy>> entry : mCarrierConfigPolicies.entrySet()) {
758             List<ErrorPolicy> errorPolicies = entry.getValue();
759             for (ErrorPolicy errorPolicy : errorPolicies) {
760                 events.addAll(errorPolicy.unthrottlingEvents());
761             }
762         }
763         for (Map.Entry<String, List<ErrorPolicy>> entry : mDefaultPolicies.entrySet()) {
764             List<ErrorPolicy> errorPolicies = entry.getValue();
765             for (ErrorPolicy errorPolicy : errorPolicies) {
766                 events.addAll(errorPolicy.unthrottlingEvents());
767             }
768         }
769         events.add(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT);
770         return events;
771     }
772 
773     /**
774      * This method is called once on initialization of this class And is also called from handler on
775      * CARRIER_CONFIG_CHANGED event. There is no race condition between both as we register for the
776      * events after the calling this method.
777      */
readFromCarrierConfig(int currentCarrierId)778     private synchronized void readFromCarrierConfig(int currentCarrierId) {
779         String carrierConfigErrorPolicy =
780                 IwlanCarrierConfig.getConfigString(
781                         mContext, mSlotId, IwlanCarrierConfig.KEY_ERROR_POLICY_CONFIG_STRING);
782         if (carrierConfigErrorPolicy == null) {
783             Log.e(LOG_TAG, "ErrorPolicy from Carrier Config is NULL");
784             mCarrierConfigPolicies.clear();
785             mCarrierConfigErrorPolicyString = null;
786             return;
787         }
788         try {
789             Map<String, List<ErrorPolicy>> errorPolicies =
790                     readErrorPolicies(new JSONArray(carrierConfigErrorPolicy));
791             if (!errorPolicies.isEmpty()) {
792                 mCarrierConfigErrorPolicyString = carrierConfigErrorPolicy;
793                 carrierId = currentCarrierId;
794                 mCarrierConfigPolicies.clear();
795                 mCarrierConfigPolicies.putAll(errorPolicies);
796             }
797         } catch (JSONException | IllegalArgumentException e) {
798             Log.e(
799                     LOG_TAG,
800                     "Unable to parse the ErrorPolicy from CarrierConfig\n"
801                             + carrierConfigErrorPolicy);
802             mCarrierConfigPolicies.clear();
803             mCarrierConfigErrorPolicyString = null;
804             e.printStackTrace();
805         }
806     }
807 
updateUnthrottlingEvents()808     private void updateUnthrottlingEvents() {
809         Set<Integer> registerEvents, unregisterEvents;
810         unregisterEvents = mUnthrottlingEvents;
811         registerEvents = getAllUnthrottlingEvents();
812         mUnthrottlingEvents = getAllUnthrottlingEvents();
813 
814         if (unregisterEvents != null) {
815             registerEvents.removeAll(unregisterEvents);
816             unregisterEvents.removeAll(mUnthrottlingEvents);
817         }
818 
819         IwlanEventListener.getInstance(mContext, mSlotId)
820                 .addEventListener(new ArrayList<>(registerEvents), mHandler);
821         if (unregisterEvents != null) {
822             IwlanEventListener.getInstance(mContext, mSlotId)
823                     .removeEventListener(new ArrayList<>(unregisterEvents), mHandler);
824         }
825         Log.d(
826                 LOG_TAG,
827                 "UnthrottlingEvents: "
828                         + (mUnthrottlingEvents != null
829                                 ? Arrays.toString(mUnthrottlingEvents.toArray())
830                                 : "null"));
831     }
832 
unthrottleLastErrorOnEvent(int event)833     private synchronized void unthrottleLastErrorOnEvent(int event) {
834         Log.d(LOG_TAG, "unthrottleLastErrorOnEvent: " + event);
835         // Pass the other events to RetryActionStore to check if can unthrottle
836         mRetryActionStoreByApn.forEach(
837                 (apn, retryActionStore) -> retryActionStore.handleUnthrottlingEvent(event));
838         // Carrier Config Changed should clear all RetryActionStore
839         if (event == IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT) {
840             mRetryActionStoreByApn.clear();
841         }
842     }
843 
844     @VisibleForTesting
getErrorStats()845     ErrorStats getErrorStats() {
846         return mErrorStats;
847     }
848 
849     @AutoValue
850     abstract static class ErrorPolicy {
851         private static final String LOG_TAG = ErrorPolicyManager.class.getSimpleName();
852 
errorType()853         abstract @ErrorPolicyErrorType int errorType();
854 
errorDetails()855         abstract List<String> errorDetails();
856 
retryArray()857         abstract List<Integer> retryArray();
858 
infiniteRetriesWithLastRetryTime()859         abstract Boolean infiniteRetriesWithLastRetryTime();
860 
unthrottlingEvents()861         abstract List<Integer> unthrottlingEvents();
862 
numAttemptsPerFqdn()863         abstract Optional<Integer> numAttemptsPerFqdn();
864 
handoverAttemptCount()865         abstract Optional<Integer> handoverAttemptCount();
866 
867         @AutoValue.Builder
868         abstract static class Builder {
setErrorType(int errorType)869             abstract Builder setErrorType(int errorType);
870 
setErrorDetails(List<String> errorDetails)871             abstract Builder setErrorDetails(List<String> errorDetails);
872 
setRetryArray(List<Integer> retryArray)873             abstract Builder setRetryArray(List<Integer> retryArray);
874 
setInfiniteRetriesWithLastRetryTime( Boolean infiniteRetriesWithLastRetryTime)875             abstract Builder setInfiniteRetriesWithLastRetryTime(
876                     Boolean infiniteRetriesWithLastRetryTime);
877 
setUnthrottlingEvents(List<Integer> unthrottlingEvents)878             abstract Builder setUnthrottlingEvents(List<Integer> unthrottlingEvents);
879 
setNumAttemptsPerFqdn(Integer numAttemptsPerFqdn)880             abstract Builder setNumAttemptsPerFqdn(Integer numAttemptsPerFqdn);
881 
setHandoverAttemptCount(Integer handoverAttemptCount)882             abstract Builder setHandoverAttemptCount(Integer handoverAttemptCount);
883 
build()884             abstract ErrorPolicy build();
885         }
886 
getRetryTime(int index)887         long getRetryTime(int index) {
888             long retryTime = -1;
889             if (retryArray().size() > 0) {
890                 // If the index is greater than or equal to the last element's index
891                 // and if the last item in the retryArray is "-1" use the retryTime
892                 // of the element before the last element to repeat the element.
893                 if (infiniteRetriesWithLastRetryTime()) {
894                     index = Math.min(index, retryArray().size() - 2);
895                 }
896                 if (index >= 0 && index < retryArray().size()) {
897                     retryTime = retryArray().get(index);
898                 }
899             }
900 
901             // retryTime -1 represents indefinite failure. In that case
902             // return time that represents 1 day to not retry for that day.
903             if (retryTime == -1L) {
904                 retryTime = TimeUnit.DAYS.toSeconds(1);
905             }
906             return retryTime;
907         }
908 
getCurrentFqdnIndex(int retryIndex, int numFqdns)909         int getCurrentFqdnIndex(int retryIndex, int numFqdns) {
910             int result = -1;
911             if (numAttemptsPerFqdn().isEmpty() || retryArray().size() <= 0) {
912                 return result;
913             }
914             // Cycles between 0 and (numFqdns - 1), based on the current attempt count and size of
915             // mRetryArray.
916             return (retryIndex + 1) / numAttemptsPerFqdn().get() % numFqdns;
917         }
918 
919         @ErrorPolicyErrorType
getErrorType()920         int getErrorType() {
921             return errorType();
922         }
923 
getHandoverAttemptCount()924         int getHandoverAttemptCount() {
925             return handoverAttemptCount().orElse(Integer.MAX_VALUE);
926         }
927 
canUnthrottle(int event)928         synchronized boolean canUnthrottle(int event) {
929             return unthrottlingEvents().contains(event);
930         }
931 
match(IwlanError iwlanError)932         boolean match(IwlanError iwlanError) {
933             // Generic by default to match to generic policy.
934             String iwlanErrorDetail;
935             if (errorType() == FALLBACK_ERROR_TYPE) {
936                 return true;
937             } else if (errorType() == IKE_PROTOCOL_ERROR_TYPE
938                     && iwlanError.getErrorType() == IwlanError.IKE_PROTOCOL_EXCEPTION) {
939                 IkeProtocolException exception = (IkeProtocolException) iwlanError.getException();
940                 iwlanErrorDetail = String.valueOf(exception.getErrorType());
941             } else if (errorType() == GENERIC_ERROR_TYPE) {
942                 iwlanErrorDetail = getGenericErrorDetailString(iwlanError);
943                 if (iwlanErrorDetail.equals("UNKNOWN")) {
944                     return false;
945                 }
946             } else {
947                 return false;
948             }
949 
950             boolean ret = false;
951             for (String errorDetail : errorDetails()) {
952                 if (errorType() == IKE_PROTOCOL_ERROR_TYPE
953                         && iwlanError.getErrorType() == IwlanError.IKE_PROTOCOL_EXCEPTION
954                         && errorDetail.contains("-")) {
955                     // error detail is stored in range format.
956                     // ErrorPolicyManager#verifyIkeProtocolErrorDetail will make sure that
957                     // this is stored correctly in "min-max" format.
958                     String[] range = errorDetail.split("-");
959                     int min = Integer.parseInt(range[0]);
960                     int max = Integer.parseInt(range[1]);
961                     int error = Integer.parseInt(iwlanErrorDetail);
962                     if (error >= min && error <= max) {
963                         ret = true;
964                         break;
965                     }
966                 } else if (errorDetail.equals(iwlanErrorDetail) || errorDetail.equals("*")) {
967                     ret = true;
968                     break;
969                 }
970             }
971             return ret;
972         }
973 
log()974         void log() {
975             Log.d(LOG_TAG, "ErrorType: " + errorType());
976             Log.d(LOG_TAG, "ErrorDetail: " + Arrays.toString(errorDetails().toArray()));
977             Log.d(LOG_TAG, "RetryArray: " + Arrays.toString(retryArray().toArray()));
978             Log.d(
979                     LOG_TAG,
980                     "InfiniteRetriesWithLastRetryTime: " + infiniteRetriesWithLastRetryTime());
981             Log.d(
982                     LOG_TAG,
983                     "UnthrottlingEvents: " + Arrays.toString(unthrottlingEvents().toArray()));
984             Log.d(LOG_TAG, "NumAttemptsPerFqdn: " + numAttemptsPerFqdn());
985             Log.d(LOG_TAG, "handoverAttemptCount: " + handoverAttemptCount());
986         }
987 
isFallback()988         boolean isFallback() {
989             return (errorType() == FALLBACK_ERROR_TYPE)
990                     || (errorDetails().size() == 1 && errorDetails().get(0).equals("*"));
991         }
992 
getGenericErrorDetailString(IwlanError iwlanError)993         String getGenericErrorDetailString(IwlanError iwlanError) {
994             String ret = "UNKNOWN";
995             switch (iwlanError.getErrorType()) {
996                 case IwlanError.IKE_INTERNAL_IO_EXCEPTION:
997                     ret = "IO_EXCEPTION";
998                     break;
999                 case IwlanError.EPDG_SELECTOR_SERVER_SELECTION_FAILED:
1000                     ret = "SERVER_SELECTION_FAILED";
1001                     break;
1002                 case IwlanError.TUNNEL_TRANSFORM_FAILED:
1003                     ret = "TUNNEL_TRANSFORM_FAILED";
1004                     break;
1005                 case IwlanError.IKE_NETWORK_LOST_EXCEPTION:
1006                     ret = "IKE_NETWORK_LOST_EXCEPTION";
1007                     break;
1008                 case IwlanError.EPDG_ADDRESS_ONLY_IPV4_ALLOWED:
1009                     ret = "EPDG_ADDRESS_ONLY_IPV4_ALLOWED";
1010                     break;
1011                 case IwlanError.EPDG_ADDRESS_ONLY_IPV6_ALLOWED:
1012                     ret = "EPDG_ADDRESS_ONLY_IPV6_ALLOWED";
1013                     break;
1014                     // TODO: Add TIMEOUT_EXCEPTION processing
1015                 case IwlanError.IKE_INIT_TIMEOUT:
1016                     ret = "IKE_INIT_TIMEOUT";
1017                     break;
1018                 case IwlanError.IKE_MOBILITY_TIMEOUT:
1019                     ret = "IKE_MOBILITY_TIMEOUT";
1020                     break;
1021                 case IwlanError.IKE_DPD_TIMEOUT:
1022                     ret = "IKE_DPD_TIMEOUT";
1023                     break;
1024             }
1025             return ret;
1026         }
1027     }
1028 
1029     /**
1030      * A data class to store the error cause and the applied error policy. This class is responsible
1031      * to calculate the retry time base on the error policy / config.
1032      */
1033     interface RetryAction {
error()1034         IwlanError error();
1035 
errorPolicy()1036         ErrorPolicy errorPolicy();
1037 
lastErrorTime()1038         long lastErrorTime();
1039 
1040         /** The total time should be waited between lastErrorTime and next retry. */
totalRetryTimeMs()1041         long totalRetryTimeMs();
1042 
1043         /** The number of same cause error observed since last success / unthrottle event. */
errorCountOfSameCause()1044         int errorCountOfSameCause();
1045 
shouldRetryWithInitialAttach()1046         boolean shouldRetryWithInitialAttach();
1047 
getCurrentFqdnIndex(int numFqdns)1048         int getCurrentFqdnIndex(int numFqdns);
1049     }
1050 
1051     /** RetryAction with retry time defined by retry index and error policy */
1052     @AutoValue
1053     abstract static class PolicyDerivedRetryAction implements RetryAction {
currentRetryIndex()1054         abstract int currentRetryIndex();
1055 
1056         @Override
totalRetryTimeMs()1057         public long totalRetryTimeMs() {
1058             return TimeUnit.SECONDS.toMillis(errorPolicy().getRetryTime(currentRetryIndex()));
1059         }
1060 
1061         @Override
getCurrentFqdnIndex(int numFqdns)1062         public int getCurrentFqdnIndex(int numFqdns) {
1063             ErrorPolicy errorPolicy = errorPolicy();
1064             return errorPolicy.getCurrentFqdnIndex(currentRetryIndex(), numFqdns);
1065         }
1066 
1067         @Override
shouldRetryWithInitialAttach()1068         public boolean shouldRetryWithInitialAttach() {
1069             // UE should only uses initial attach to reset network failure, not for UE internal or
1070             // DNS errors. When the number of handover failures due to network issues exceeds the
1071             // configured threshold, UE should request network with initial attach instead of
1072             // handover request.
1073             ErrorPolicy errorPolicy = errorPolicy();
1074             return errorPolicy.getErrorType() == IKE_PROTOCOL_ERROR_TYPE
1075                     && currentRetryIndex() + 1 >= errorPolicy.getHandoverAttemptCount();
1076         }
1077 
1078         /** Create a new PolicyDerivedRetryAction */
create( IwlanError error, ErrorPolicy errorPolicy, int errorCountOfSameCause, int currentRetryIndex)1079         static PolicyDerivedRetryAction create(
1080                 IwlanError error,
1081                 ErrorPolicy errorPolicy,
1082                 int errorCountOfSameCause,
1083                 int currentRetryIndex) {
1084             return new AutoValue_ErrorPolicyManager_PolicyDerivedRetryAction(
1085                     error,
1086                     errorPolicy,
1087                     IwlanHelper.elapsedRealtime(),
1088                     errorCountOfSameCause,
1089                     currentRetryIndex);
1090         }
1091     }
1092 
1093     /** RetryAction with retry time defined by backoff time in tunnel config */
1094     @AutoValue
1095     abstract static class IkeBackoffNotifyRetryAction implements RetryAction {
backoffTime()1096         abstract long backoffTime();
1097 
1098         @Override
totalRetryTimeMs()1099         public long totalRetryTimeMs() {
1100             return TimeUnit.SECONDS.toMillis(backoffTime());
1101         }
1102 
1103         @Override
getCurrentFqdnIndex(int numFqdns)1104         public int getCurrentFqdnIndex(int numFqdns) {
1105             // Not applicable for backoff time configured case, therefore returning 0 here
1106             return 0;
1107         }
1108 
1109         @Override
shouldRetryWithInitialAttach()1110         public boolean shouldRetryWithInitialAttach() {
1111             // TODO(b/308745683): Initial attach condition is undefined for backoff config case
1112             ErrorPolicy errorPolicy = errorPolicy();
1113             return errorPolicy.getErrorType() == IKE_PROTOCOL_ERROR_TYPE
1114                     && errorPolicy.getHandoverAttemptCount() == 0;
1115         }
1116 
create( IwlanError error, ErrorPolicy errorPolicy, int errorCountOfSameCause, long backoffTime)1117         static IkeBackoffNotifyRetryAction create(
1118                 IwlanError error,
1119                 ErrorPolicy errorPolicy,
1120                 int errorCountOfSameCause,
1121                 long backoffTime) {
1122             return new AutoValue_ErrorPolicyManager_IkeBackoffNotifyRetryAction(
1123                     error,
1124                     errorPolicy,
1125                     IwlanHelper.elapsedRealtime(),
1126                     errorCountOfSameCause,
1127                     backoffTime);
1128         }
1129     }
1130 
1131     interface ErrorCause {
1132         @IwlanError.IwlanErrorType
iwlanErrorType()1133         int iwlanErrorType();
1134 
fromIwlanError(IwlanError iwlanError)1135         static ErrorCause fromIwlanError(IwlanError iwlanError) {
1136             if (iwlanError.getErrorType() == IwlanError.IKE_PROTOCOL_EXCEPTION) {
1137                 return new AutoValue_ErrorPolicyManager_IkeProtocolErrorCause(
1138                         /* ikeProtocolErrorType= */ ((IkeProtocolException)
1139                                         iwlanError.getException())
1140                                 .getErrorType());
1141             }
1142             return new AutoValue_ErrorPolicyManager_NonIkeProtocolErrorCause(
1143                     /* iwlanErrorType= */ iwlanError.getErrorType());
1144         }
1145     }
1146 
1147     @AutoValue
1148     abstract static class NonIkeProtocolErrorCause implements ErrorCause {}
1149 
1150     /**
1151      * An IkeProtocolErrorCause will carry the ike protocol error type, so that different protocol
1152      * error will be treated as different error cause
1153      */
1154     @AutoValue
1155     abstract static class IkeProtocolErrorCause implements ErrorCause {
1156         @Override
1157         @IwlanError.IwlanErrorType
iwlanErrorType()1158         public int iwlanErrorType() {
1159             return IwlanError.IKE_PROTOCOL_EXCEPTION;
1160         }
1161 
1162         // @IkeProtocolException.ErrorType is hidden API
ikeProtocolErrorType()1163         abstract int ikeProtocolErrorType();
1164     }
1165 
1166     /**
1167      * This class manage and store the RetryAction of the APN, and responsible to create RetryAction
1168      * when IwlanError received.
1169      */
1170     class ApnRetryActionStore {
1171         final String mApn;
1172         final ConcurrentHashMap<ErrorCause, RetryAction> mLastRetryActionByCause;
1173         @Nullable RetryAction mLastRetryAction;
1174 
ApnRetryActionStore(String apn)1175         ApnRetryActionStore(String apn) {
1176             mApn = apn;
1177             mLastRetryActionByCause = new ConcurrentHashMap<>();
1178         }
1179 
1180         /**
1181          * Determines whether the new {@link RetryAction} should accumulate the retry index from
1182          * {@code prevRetryAction}.
1183          *
1184          * @param prevRetryAction the previous RetryAction (can be null).
1185          * @param newIwlanError the new IwlanError.
1186          * @return true if {@code prevRetryAction} is an instance of {@link
1187          *     PolicyDerivedRetryAction} and is the same {@link ErrorCause} as {@code
1188          *     newIwlanError}, false otherwise.
1189          */
shouldAccumulateRetryIndex( @ullable RetryAction prevRetryAction, IwlanError newIwlanError)1190         private boolean shouldAccumulateRetryIndex(
1191                 @Nullable RetryAction prevRetryAction, IwlanError newIwlanError) {
1192             if (!(prevRetryAction instanceof PolicyDerivedRetryAction)) {
1193                 return false;
1194             }
1195 
1196             boolean isSameIwlanError = prevRetryAction.error().equals(newIwlanError);
1197             // If prev and current error are both IKE_PROTOCOL_EXCEPTION, keep the retry index
1198             // TODO: b/292312000 - Workaround for RetryIndex lost
1199             boolean areBothIkeProtocolException =
1200                     (newIwlanError.getErrorType() == IwlanError.IKE_PROTOCOL_EXCEPTION
1201                             && prevRetryAction.error().getErrorType()
1202                                     == IwlanError.IKE_PROTOCOL_EXCEPTION);
1203             boolean shouldAccumulateRetryIndex = isSameIwlanError || areBothIkeProtocolException;
1204 
1205             if (!shouldAccumulateRetryIndex) {
1206                 Log.d(LOG_TAG, "Doesn't match to the previous error" + newIwlanError);
1207             }
1208 
1209             return shouldAccumulateRetryIndex;
1210         }
1211 
generateRetryAction(IwlanError iwlanError)1212         private PolicyDerivedRetryAction generateRetryAction(IwlanError iwlanError) {
1213             ErrorCause errorCause = ErrorCause.fromIwlanError(iwlanError);
1214 
1215             @Nullable RetryAction prevRetryAction = mLastRetryActionByCause.get(errorCause);
1216             int newErrorCount =
1217                     prevRetryAction != null ? prevRetryAction.errorCountOfSameCause() + 1 : 1;
1218             boolean shouldAccumulateRetryIndex =
1219                     shouldAccumulateRetryIndex(prevRetryAction, iwlanError);
1220             int newRetryIndex =
1221                     shouldAccumulateRetryIndex
1222                             ? ((PolicyDerivedRetryAction) prevRetryAction).currentRetryIndex() + 1
1223                             : 0;
1224 
1225             ErrorPolicy policy = findErrorPolicy(mApn, iwlanError);
1226             PolicyDerivedRetryAction newRetryAction =
1227                     PolicyDerivedRetryAction.create(
1228                             iwlanError, policy, newErrorCount, newRetryIndex);
1229             mLastRetryActionByCause.put(errorCause, newRetryAction);
1230             mLastRetryAction = newRetryAction;
1231 
1232             return newRetryAction;
1233         }
1234 
generateRetryAction( IwlanError iwlanError, long backoffTime)1235         private IkeBackoffNotifyRetryAction generateRetryAction(
1236                 IwlanError iwlanError, long backoffTime) {
1237             ErrorCause errorCause = ErrorCause.fromIwlanError(iwlanError);
1238             @Nullable RetryAction prevRetryAction = mLastRetryActionByCause.get(errorCause);
1239             int newErrorCount =
1240                     prevRetryAction != null ? prevRetryAction.errorCountOfSameCause() + 1 : 1;
1241             ErrorPolicy policy = findErrorPolicy(mApn, iwlanError);
1242             // For configured back off time case, simply create new RetryAction, nothing need to
1243             // keep
1244             IkeBackoffNotifyRetryAction newRetryAction =
1245                     IkeBackoffNotifyRetryAction.create(
1246                             iwlanError, policy, newErrorCount, backoffTime);
1247             mLastRetryActionByCause.put(errorCause, newRetryAction);
1248             mLastRetryAction = newRetryAction;
1249 
1250             return newRetryAction;
1251         }
1252 
1253         /**
1254          * Set {@code lastRetryAction} to null if {@code lastRetryAction} can be unthrottled by the
1255          * event. Clear those reserved retry index and the {@link RetryAction} if any {@link
1256          * RetryAction} in {@code mLastRetryActionByCause} can be unthrottled by the event.
1257          *
1258          * @param event the handling event
1259          */
handleUnthrottlingEvent(int event)1260         private void handleUnthrottlingEvent(int event) {
1261             if (event == IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT) {
1262                 mLastRetryActionByCause.clear();
1263             } else {
1264                 // Check all stored RetryAction, remove from the store if it can be unthrottle.
1265                 // By removing it, the retry index (for PolicyDerived) will reset as 0
1266                 mLastRetryActionByCause
1267                         .entrySet()
1268                         .removeIf(it -> it.getValue().errorPolicy().canUnthrottle(event));
1269             }
1270 
1271             DataService.DataServiceProvider provider =
1272                     IwlanDataService.getDataServiceProvider(mSlotId);
1273 
1274             boolean isCarrierConfigChanged =
1275                     event == IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT;
1276             boolean isLastRetryActionCanUnthrottle =
1277                     mLastRetryAction != null && mLastRetryAction.errorPolicy().canUnthrottle(event);
1278             if (isCarrierConfigChanged || isLastRetryActionCanUnthrottle) {
1279                 mLastRetryAction = null;
1280 
1281                 if (provider == null) {
1282                     Log.w(LOG_TAG, "DataServiceProvider not found for slot: " + mSlotId);
1283                 } else {
1284                     provider.notifyApnUnthrottled(mApn);
1285                     Log.d(LOG_TAG, "unthrottled error for: " + mApn);
1286                 }
1287             }
1288         }
1289 
1290         @Nullable
getLastRetryAction()1291         private RetryAction getLastRetryAction() {
1292             return mLastRetryAction;
1293         }
1294     }
1295 
1296     static class ApnWithIwlanError {
1297         @NonNull final String mApn;
1298         @NonNull final IwlanError mIwlanError;
1299 
ApnWithIwlanError(@onNull String apn, @NonNull IwlanError iwlanError)1300         ApnWithIwlanError(@NonNull String apn, @NonNull IwlanError iwlanError) {
1301             mApn = apn;
1302             mIwlanError = iwlanError;
1303         }
1304     }
1305 
isValidCarrierConfigChangedEvent(int currentCarrierId)1306     private boolean isValidCarrierConfigChangedEvent(int currentCarrierId) {
1307         String errorPolicyConfig =
1308                 IwlanCarrierConfig.getConfigString(
1309                         mContext, mSlotId, IwlanCarrierConfig.KEY_ERROR_POLICY_CONFIG_STRING);
1310         return (currentCarrierId != carrierId)
1311                 || (mCarrierConfigErrorPolicyString == null)
1312                 || (errorPolicyConfig != null
1313                         && !Objects.equals(mCarrierConfigErrorPolicyString, errorPolicyConfig));
1314     }
1315 
1316     private final class EpmHandler extends Handler {
1317         private final String TAG = EpmHandler.class.getSimpleName();
1318 
1319         @Override
handleMessage(Message msg)1320         public void handleMessage(Message msg) {
1321             Log.d(TAG, "msg.what = " + msg.what);
1322             switch (msg.what) {
1323                 case IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT:
1324                     Log.d(TAG, "On CARRIER_CONFIG_CHANGED_EVENT");
1325                     int currentCarrierId = IwlanHelper.getCarrierId(mContext, mSlotId);
1326                     if (isValidCarrierConfigChangedEvent(currentCarrierId)) {
1327                         Log.d(TAG, "Unthrottle last error and read from carrier config");
1328                         unthrottleLastErrorOnEvent(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT);
1329                         readFromCarrierConfig(currentCarrierId);
1330                         updateUnthrottlingEvents();
1331                     }
1332                     break;
1333                 case IwlanEventListener.APM_ENABLE_EVENT:
1334                 case IwlanEventListener.APM_DISABLE_EVENT:
1335                 case IwlanEventListener.WIFI_DISABLE_EVENT:
1336                 case IwlanEventListener.WIFI_CALLING_DISABLE_EVENT:
1337                 case IwlanEventListener.WIFI_AP_CHANGED_EVENT:
1338                     unthrottleLastErrorOnEvent(msg.what);
1339                     break;
1340                 default:
1341                     Log.d(TAG, "Unknown message received!");
1342                     break;
1343             }
1344         }
1345 
EpmHandler(Looper looper)1346         EpmHandler(Looper looper) {
1347             super(looper);
1348         }
1349     }
1350 
1351     @VisibleForTesting
1352     static class ErrorStats {
1353         @VisibleForTesting Map<String, Map<String, Long>> mStats = new HashMap<>();
1354         private Date mStartTime;
1355         private int mStatCount;
1356         private static final int APN_COUNT_MAX = 10;
1357         private static final int ERROR_COUNT_MAX = 1000;
1358 
ErrorStats()1359         ErrorStats() {
1360             mStartTime = Calendar.getInstance().getTime();
1361             mStatCount = 0;
1362         }
1363 
update(String apn, IwlanError error)1364         void update(String apn, IwlanError error) {
1365             if (mStats.size() >= APN_COUNT_MAX || mStatCount >= ERROR_COUNT_MAX) {
1366                 reset();
1367             }
1368             if (!mStats.containsKey(apn)) {
1369                 mStats.put(apn, new HashMap<>());
1370             }
1371             Map<String, Long> errorMap = mStats.get(apn);
1372             String errorString = error.toString();
1373             if (!errorMap.containsKey(errorString)) {
1374                 errorMap.put(errorString, 0L);
1375             }
1376             long count = errorMap.get(errorString);
1377             errorMap.put(errorString, ++count);
1378             mStats.put(apn, errorMap);
1379             mStatCount++;
1380         }
1381 
reset()1382         void reset() {
1383             mStartTime = Calendar.getInstance().getTime();
1384             mStats = new HashMap<>();
1385             mStatCount = 0;
1386         }
1387 
1388         @Override
toString()1389         public String toString() {
1390             StringBuilder sb = new StringBuilder();
1391             sb.append("mStartTime: ").append(mStartTime);
1392             sb.append("\nErrorStats");
1393             for (Map.Entry<String, Map<String, Long>> entry : mStats.entrySet()) {
1394                 sb.append("\n\tApn: ").append(entry.getKey());
1395                 for (Map.Entry<String, Long> errorEntry : entry.getValue().entrySet()) {
1396                     sb.append("\n\t  ")
1397                             .append(errorEntry.getKey())
1398                             .append(" : ")
1399                             .append(errorEntry.getValue());
1400                 }
1401             }
1402             return sb.toString();
1403         }
1404     }
1405 }
1406