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