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