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