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