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.DataCallResponse; 31 import android.telephony.data.DataService; 32 import android.text.TextUtils; 33 import android.util.Log; 34 35 import com.android.internal.annotations.VisibleForTesting; 36 37 import com.google.auto.value.AutoValue; 38 39 import org.json.JSONArray; 40 import org.json.JSONException; 41 import org.json.JSONObject; 42 43 import java.io.PrintWriter; 44 import java.time.Duration; 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.Objects; 54 import java.util.Optional; 55 import java.util.Set; 56 import java.util.concurrent.ConcurrentHashMap; 57 import java.util.concurrent.TimeUnit; 58 59 public class ErrorPolicyManager { 60 61 /** 62 * This type is not to be used in config. This is only used internally to catch errors in 63 * parsing the error type. 64 */ 65 private static final int UNKNOWN_ERROR_TYPE = -1; 66 67 /** 68 * This value represents that the error tye is to be used as a fallback to represent all the 69 * errors. 70 */ 71 private static final int FALLBACK_ERROR_TYPE = 1; 72 73 /** 74 * This value represents rest of the errors that are not defined above. ErrorDetails should 75 * mention the specific error. If it doesn't - the policy will be used as a fallback global 76 * policy. Currently, Supported ErrorDetails "IO_EXCEPTION" "TIMEOUT_EXCEPTION" 77 * "SERVER_SELECTION_FAILED" "TUNNEL_TRANSFORM_FAILED" 78 */ 79 private static final int GENERIC_ERROR_TYPE = 2; 80 81 /** 82 * This value represents IKE Protocol Error/Notify Error. 83 * 84 * @see <a href="https://tools.ietf.org/html/rfc4306#section-3.10.1">RFC 4306,Internet Key 85 * Exchange (IKEv2) Protocol </a> for global errors and carrier specific requirements for 86 * other carrier specific error codes. ErrorDetails defined for this type is always in 87 * numeric form representing the error codes. Examples: "24", "9000-9050" 88 */ 89 private static final int IKE_PROTOCOL_ERROR_TYPE = 3; 90 builder()91 static ErrorPolicy.Builder builder() { 92 return new AutoValue_ErrorPolicyManager_ErrorPolicy.Builder() 93 .setInfiniteRetriesWithLastRetryTime(false); 94 } 95 96 @IntDef({UNKNOWN_ERROR_TYPE, FALLBACK_ERROR_TYPE, GENERIC_ERROR_TYPE, IKE_PROTOCOL_ERROR_TYPE}) 97 @interface ErrorPolicyErrorType {} 98 99 private static final String[] GENERIC_ERROR_DETAIL_STRINGS = { 100 "*", 101 "IO_EXCEPTION", 102 "TIMEOUT_EXCEPTION", 103 "SERVER_SELECTION_FAILED", 104 "TUNNEL_TRANSFORM_FAILED" 105 }; 106 107 /** Private IKEv2 notify message types. As defined in TS 124 302 (section 8.1.2.2) */ 108 private static final int IKE_PROTOCOL_ERROR_PDN_CONNECTION_REJECTION = 8192; 109 110 private static final int IKE_PROTOCOL_ERROR_MAX_CONNECTION_REACHED = 8193; 111 private static final int IKE_PROTOCOL_ERROR_SEMANTIC_ERROR_IN_THE_TFT_OPERATION = 8241; 112 private static final int IKE_PROTOCOL_ERROR_SYNTACTICAL_ERROR_IN_THE_TFT_OPERATION = 8242; 113 private static final int IKE_PROTOCOL_ERROR_SEMANTIC_ERRORS_IN_PACKET_FILTERS = 8244; 114 private static final int IKE_PROTOCOL_ERROR_SYNTACTICAL_ERRORS_IN_PACKET_FILTERS = 8245; 115 private static final int IKE_PROTOCOL_ERROR_NON_3GPP_ACCESS_TO_EPC_NOT_ALLOWED = 9000; 116 private static final int IKE_PROTOCOL_ERROR_USER_UNKNOWN = 9001; 117 private static final int IKE_PROTOCOL_ERROR_NO_APN_SUBSCRIPTION = 9002; 118 private static final int IKE_PROTOCOL_ERROR_AUTHORIZATION_REJECTED = 9003; 119 private static final int IKE_PROTOCOL_ERROR_ILLEGAL_ME = 9006; 120 private static final int IKE_PROTOCOL_ERROR_NETWORK_FAILURE = 10500; 121 private static final int IKE_PROTOCOL_ERROR_RAT_TYPE_NOT_ALLOWED = 11001; 122 private static final int IKE_PROTOCOL_ERROR_IMEI_NOT_ACCEPTED = 11005; 123 private static final int IKE_PROTOCOL_ERROR_PLMN_NOT_ALLOWED = 11011; 124 private static final int IKE_PROTOCOL_ERROR_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED = 11055; 125 126 /** 127 * Represents the retry backoff duration is unspecified, see {@link 128 * android.telephony.data.DataCallResponse#RETRY_DURATION_UNDEFINED} 129 */ 130 public static final Duration UNSPECIFIED_RETRY_DURATION = 131 Duration.ofMillis(DataCallResponse.RETRY_DURATION_UNDEFINED); 132 133 /** Private IKEv2 notify message types, as defined in TS 124 502 (section 9.2.4.1) */ 134 private static final int IKE_PROTOCOL_ERROR_CONGESTION = 15500; 135 136 private static final ErrorPolicy FALLBACK_ERROR_POLICY = 137 builder() 138 .setErrorType(FALLBACK_ERROR_TYPE) 139 .setRetryArray(List.of(5, -1)) 140 .setErrorDetails(List.of("*")) 141 .setUnthrottlingEvents(List.of()) 142 .build(); 143 144 private final String LOG_TAG; 145 146 private static final Map<Integer, ErrorPolicyManager> mInstances = new ConcurrentHashMap<>(); 147 private final Context mContext; 148 private final int mSlotId; 149 150 // Policies read from defaultiwlanerrorconfig.json 151 // String APN as key to identify the ErrorPolicies associated with it. 152 private final Map<String, List<ErrorPolicy>> mDefaultPolicies = new HashMap<>(); 153 154 // Policies read from CarrierConfig 155 // String APN as key to identify the ErrorPolicies associated with it. 156 private final Map<String, List<ErrorPolicy>> mCarrierConfigPolicies = new HashMap<>(); 157 158 /** String APN as key to identify the {@link ApnRetryActionStore} associated with that APN */ 159 private final Map<String, ApnRetryActionStore> mRetryActionStoreByApn = 160 new ConcurrentHashMap<>(); 161 162 // Records the most recently reported IwlanError (including NO_ERROR), and the corresponding 163 // APN. 164 private ApnWithIwlanError mMostRecentError; 165 166 // List of current Unthrottling events registered with IwlanEventListener 167 private Set<Integer> mUnthrottlingEvents; 168 169 private final ErrorStats mErrorStats = new ErrorStats(); 170 171 private HandlerThread mHandlerThread; 172 @VisibleForTesting Handler mHandler; 173 174 private int carrierId = TelephonyManager.UNKNOWN_CARRIER_ID; 175 176 private String mCarrierConfigErrorPolicyString; 177 178 /** 179 * Returns ErrorPolicyManager instance for the subId 180 * 181 * @param context the context to be used by the ErrorPolicyManager 182 * @param slotId the slot ID for which the ErrorPolicyManager instance is required 183 */ getInstance(@onNull Context context, int slotId)184 public static ErrorPolicyManager getInstance(@NonNull Context context, int slotId) { 185 return mInstances.computeIfAbsent(slotId, k -> new ErrorPolicyManager(context, slotId)); 186 } 187 188 @VisibleForTesting resetAllInstances()189 public static void resetAllInstances() { 190 mInstances.clear(); 191 } 192 193 /** Release or reset the instance. */ releaseInstance()194 public void releaseInstance() { 195 Log.d(LOG_TAG, "Release Instance with slotId: " + mSlotId); 196 IwlanEventListener.getInstance(mContext, mSlotId).removeEventListener(mHandler); 197 mHandlerThread.quit(); 198 mInstances.remove(mSlotId); 199 } 200 201 /** 202 * Updates the last error details and returns the retry time. Return value is -1, which should 203 * be ignored, when the error is IwlanError.NO_ERROR. 204 * 205 * @param apn apn name for which the error happened 206 * @param iwlanError Error 207 * @return retry time. 0 = immediate retry, -1 = fail and n = retry after n seconds 208 */ reportIwlanError(String apn, IwlanError iwlanError)209 public synchronized long reportIwlanError(String apn, IwlanError iwlanError) { 210 // Fail by default 211 mMostRecentError = new ApnWithIwlanError(apn, iwlanError); 212 213 if (iwlanError.getErrorType() == IwlanError.NO_ERROR) { 214 Log.d(LOG_TAG, "reportIwlanError: NO_ERROR"); 215 mRetryActionStoreByApn.remove(apn); 216 return DataCallResponse.RETRY_DURATION_UNDEFINED; 217 } 218 mErrorStats.update(apn, iwlanError); 219 220 PolicyDerivedRetryAction newRetryAction = 221 mRetryActionStoreByApn 222 .computeIfAbsent(apn, ApnRetryActionStore::new) 223 .generateRetryAction(iwlanError); 224 225 Log.d( 226 LOG_TAG, 227 "Current RetryAction index: " 228 + newRetryAction.currentRetryIndex() 229 + " and time: " 230 + newRetryAction.totalBackoffDuration()); 231 return newRetryAction.totalBackoffDuration().toSeconds(); 232 } 233 234 /** 235 * Updates the last error details with backoff time. 236 * 237 * @param apn apn name for which the error happened 238 * @param iwlanError Error 239 * @param backoffTime in seconds 240 * @return retry time which is the backoff time. -1 if it is {@link IwlanError#NO_ERROR} 241 */ reportIwlanError(String apn, IwlanError iwlanError, long backoffTime)242 public synchronized long reportIwlanError(String apn, IwlanError iwlanError, long backoffTime) { 243 // Fail by default 244 if (iwlanError.getErrorType() == IwlanError.NO_ERROR) { 245 Log.d(LOG_TAG, "reportIwlanError: NO_ERROR"); 246 mRetryActionStoreByApn.remove(apn); 247 return DataCallResponse.RETRY_DURATION_UNDEFINED; 248 } 249 mErrorStats.update(apn, iwlanError); 250 251 IkeBackoffNotifyRetryAction newRetryAction = 252 mRetryActionStoreByApn 253 .computeIfAbsent(apn, ApnRetryActionStore::new) 254 .generateRetryAction(iwlanError, Duration.ofSeconds(backoffTime)); 255 Log.d(LOG_TAG, "Current configured backoff time: " + newRetryAction.totalBackoffDuration); 256 257 return newRetryAction.totalBackoffDuration.toSeconds(); 258 } 259 260 /** 261 * Checks whether we can bring up Epdg Tunnel - Based on lastErrorForApn 262 * 263 * @param apn apn for which tunnel bring up needs to be checked 264 * @return true if tunnel can be brought up, false otherwise 265 */ canBringUpTunnel(String apn)266 public synchronized boolean canBringUpTunnel(String apn) { 267 RetryAction lastRetryAction = getLastRetryAction(apn); 268 boolean canBringUp = 269 lastRetryAction == null 270 || getRemainingBackoffDuration(lastRetryAction).isNegative() 271 || getRemainingBackoffDuration(lastRetryAction).isZero(); 272 Log.d(LOG_TAG, "canBringUpTunnel: " + canBringUp); 273 return canBringUp; 274 } 275 276 // TODO: Modify framework/base/Android.bp to get access to Annotation.java to use 277 // @DataFailureCause 278 // annotation as return type here. (after moving to aosp?) 279 /** 280 * Returns the DataFailCause based on the lastErrorForApn 281 * 282 * @param apn apn name for which DataFailCause is needed 283 * @return DataFailCause corresponding to the error for the apn 284 */ getDataFailCause(String apn)285 public synchronized int getDataFailCause(String apn) { 286 RetryAction lastRetryAction = getLastRetryAction(apn); 287 return lastRetryAction == null 288 ? DataFailCause.NONE 289 : getDataFailCause(lastRetryAction.error()); 290 } 291 getDataFailCause(IwlanError error)292 private int getDataFailCause(IwlanError error) { 293 int errorType = error.getErrorType(); 294 return switch (errorType) { 295 case IwlanError.NO_ERROR -> DataFailCause.NONE; 296 case IwlanError.IKE_PROTOCOL_EXCEPTION -> 297 getDataFailCauseForIkeProtocolException(error.getException()); 298 case IwlanError.IKE_INTERNAL_IO_EXCEPTION -> DataFailCause.IWLAN_IKEV2_MSG_TIMEOUT; 299 case IwlanError.IKE_GENERIC_EXCEPTION -> DataFailCause.ERROR_UNSPECIFIED; 300 case IwlanError.EPDG_SELECTOR_SERVER_SELECTION_FAILED -> 301 DataFailCause.IWLAN_DNS_RESOLUTION_NAME_FAILURE; 302 case IwlanError.TUNNEL_TRANSFORM_FAILED -> DataFailCause.IWLAN_TUNNEL_TRANSFORM_FAILED; 303 case IwlanError.SIM_NOT_READY_EXCEPTION -> DataFailCause.SIM_CARD_CHANGED; 304 case IwlanError.IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED -> 305 DataFailCause.IWLAN_IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED; 306 case IwlanError.IKE_NETWORK_LOST_EXCEPTION -> 307 DataFailCause.IWLAN_IKE_NETWORK_LOST_EXCEPTION; 308 case IwlanError.TUNNEL_NOT_FOUND -> DataFailCause.IWLAN_TUNNEL_NOT_FOUND; 309 case IwlanError.EPDG_ADDRESS_ONLY_IPV4_ALLOWED -> DataFailCause.ONLY_IPV4_ALLOWED; 310 case IwlanError.EPDG_ADDRESS_ONLY_IPV6_ALLOWED -> DataFailCause.ONLY_IPV6_ALLOWED; 311 case IwlanError.IKE_INIT_TIMEOUT -> DataFailCause.IWLAN_IKE_INIT_TIMEOUT; 312 case IwlanError.IKE_MOBILITY_TIMEOUT -> DataFailCause.IWLAN_IKE_MOBILITY_TIMEOUT; 313 case IwlanError.IKE_DPD_TIMEOUT -> DataFailCause.IWLAN_IKE_DPD_TIMEOUT; 314 default -> DataFailCause.ERROR_UNSPECIFIED; 315 }; 316 } 317 318 // TODO: create DFC for all IkeProtocolExceptions and assign here. getDataFailCauseForIkeProtocolException(Exception exception)319 private int getDataFailCauseForIkeProtocolException(Exception exception) { 320 if (!(exception instanceof IkeProtocolException ikeProtocolException)) { 321 return DataFailCause.IWLAN_IKE_PRIVATE_PROTOCOL_ERROR; 322 } 323 324 int protocolErrorType = ikeProtocolException.getErrorType(); 325 return switch (protocolErrorType) { 326 case IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED -> 327 DataFailCause.IWLAN_IKEV2_AUTH_FAILURE; 328 case IkeProtocolException.ERROR_TYPE_INTERNAL_ADDRESS_FAILURE -> 329 DataFailCause.IWLAN_EPDG_INTERNAL_ADDRESS_FAILURE; 330 case IKE_PROTOCOL_ERROR_PDN_CONNECTION_REJECTION -> 331 DataFailCause.IWLAN_PDN_CONNECTION_REJECTION; 332 case IKE_PROTOCOL_ERROR_MAX_CONNECTION_REACHED -> 333 DataFailCause.IWLAN_MAX_CONNECTION_REACHED; 334 case IKE_PROTOCOL_ERROR_SEMANTIC_ERROR_IN_THE_TFT_OPERATION -> 335 DataFailCause.IWLAN_SEMANTIC_ERROR_IN_THE_TFT_OPERATION; 336 case IKE_PROTOCOL_ERROR_SYNTACTICAL_ERROR_IN_THE_TFT_OPERATION -> 337 DataFailCause.IWLAN_SYNTACTICAL_ERROR_IN_THE_TFT_OPERATION; 338 case IKE_PROTOCOL_ERROR_SEMANTIC_ERRORS_IN_PACKET_FILTERS -> 339 DataFailCause.IWLAN_SEMANTIC_ERRORS_IN_PACKET_FILTERS; 340 case IKE_PROTOCOL_ERROR_SYNTACTICAL_ERRORS_IN_PACKET_FILTERS -> 341 DataFailCause.IWLAN_SYNTACTICAL_ERRORS_IN_PACKET_FILTERS; 342 case IKE_PROTOCOL_ERROR_NON_3GPP_ACCESS_TO_EPC_NOT_ALLOWED -> 343 DataFailCause.IWLAN_NON_3GPP_ACCESS_TO_EPC_NOT_ALLOWED; 344 case IKE_PROTOCOL_ERROR_USER_UNKNOWN -> DataFailCause.IWLAN_USER_UNKNOWN; 345 case IKE_PROTOCOL_ERROR_NO_APN_SUBSCRIPTION -> DataFailCause.IWLAN_NO_APN_SUBSCRIPTION; 346 case IKE_PROTOCOL_ERROR_AUTHORIZATION_REJECTED -> 347 DataFailCause.IWLAN_AUTHORIZATION_REJECTED; 348 case IKE_PROTOCOL_ERROR_ILLEGAL_ME -> DataFailCause.IWLAN_ILLEGAL_ME; 349 case IKE_PROTOCOL_ERROR_NETWORK_FAILURE -> DataFailCause.IWLAN_NETWORK_FAILURE; 350 case IKE_PROTOCOL_ERROR_RAT_TYPE_NOT_ALLOWED -> 351 DataFailCause.IWLAN_RAT_TYPE_NOT_ALLOWED; 352 case IKE_PROTOCOL_ERROR_IMEI_NOT_ACCEPTED -> DataFailCause.IWLAN_IMEI_NOT_ACCEPTED; 353 case IKE_PROTOCOL_ERROR_PLMN_NOT_ALLOWED -> DataFailCause.IWLAN_PLMN_NOT_ALLOWED; 354 case IKE_PROTOCOL_ERROR_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED -> 355 DataFailCause.IWLAN_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED; 356 case IKE_PROTOCOL_ERROR_CONGESTION -> DataFailCause.IWLAN_CONGESTION; 357 default -> DataFailCause.IWLAN_IKE_PRIVATE_PROTOCOL_ERROR; 358 }; 359 } 360 361 public synchronized int getMostRecentDataFailCause() { 362 if (mMostRecentError != null) { 363 return getDataFailCause(mMostRecentError.mIwlanError); 364 } 365 return DataFailCause.NONE; 366 } 367 368 /** 369 * Returns the current remaining backoff duration of the APN 370 * 371 * @param apn APN name for which current backoff duration is needed 372 * @return long current retry time in milliseconds 373 */ 374 public synchronized Duration getRemainingBackoffDuration(String apn) { 375 RetryAction lastRetryAction = getLastRetryAction(apn); 376 return lastRetryAction == null 377 ? UNSPECIFIED_RETRY_DURATION 378 : getRemainingBackoffDuration(lastRetryAction); 379 } 380 381 /** 382 * Returns the current remaining backoff duration based on the last retryAction time 383 * 384 * @param retryAction the last error 385 */ 386 private static Duration getRemainingBackoffDuration(RetryAction retryAction) { 387 Duration totalBackoffDuration = retryAction.totalBackoffDuration(); 388 long errorTime = retryAction.lastErrorTime(); 389 long currentTime = IwlanHelper.elapsedRealtime(); 390 Duration sinceLastErrorDuration = Duration.ofMillis(currentTime - errorTime); 391 Duration remainingBackupDuration = totalBackoffDuration.minus(sinceLastErrorDuration); 392 return remainingBackupDuration.isNegative() ? Duration.ZERO : remainingBackupDuration; 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 */ 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 */ 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 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 */ 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 */ 448 public synchronized boolean shouldRetryWithInitialAttach(String apn) { 449 RetryAction retryAction = getLastRetryAction(apn); 450 return retryAction != null && retryAction.shouldRetryWithInitialAttach(); 451 } 452 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 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 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 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 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 556 void initHandler() { 557 mHandler = new EpmHandler(getLooper()); 558 } 559 560 @VisibleForTesting 561 Looper getLooper() { 562 mHandlerThread = new HandlerThread("ErrorPolicyManagerThread"); 563 mHandlerThread.start(); 564 return mHandlerThread.getLooper(); 565 } 566 567 @VisibleForTesting 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 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 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 679 private List<String> parseErrorDetails(int errorType, JSONArray errorDetailArray) 680 throws JSONException, IllegalArgumentException { 681 List<String> ret = new ArrayList<>(); 682 683 for (int i = 0; i < errorDetailArray.length(); i++) { 684 String errorDetail = errorDetailArray.getString(i).trim(); 685 boolean isValidErrorDetail = 686 switch (errorType) { 687 case IKE_PROTOCOL_ERROR_TYPE -> verifyIkeProtocolErrorDetail(errorDetail); 688 case GENERIC_ERROR_TYPE -> verifyGenericErrorDetail(errorDetail); 689 default -> true; 690 }; 691 if (!isValidErrorDetail) { 692 throw new IllegalArgumentException( 693 "Invalid ErrorDetail: " + errorDetail + " for ErrorType: " + errorType); 694 } 695 ret.add(errorDetail); 696 } 697 return ret; 698 } 699 700 /** Allowed formats are: number(Integer), range(Integers separated by -) and "*" */ 701 private boolean verifyIkeProtocolErrorDetail(String errorDetailStr) { 702 boolean ret = true; 703 if (errorDetailStr.contains("-")) { 704 // verify range format 705 String[] rangeNumbers = errorDetailStr.split("-"); 706 if (rangeNumbers.length == 2) { 707 for (String range : rangeNumbers) { 708 if (!TextUtils.isDigitsOnly(range)) { 709 ret = false; 710 } 711 } 712 } else { 713 ret = false; 714 } 715 } else if (!errorDetailStr.equals("*") && !TextUtils.isDigitsOnly(errorDetailStr)) { 716 ret = false; 717 } 718 return ret; 719 } 720 721 /** 722 * Allowed strings are: "IO_EXCEPTION", "TIMEOUT_EXCEPTION", "SERVER_SELECTION_FAILED", 723 * "TUNNEL_TRANSFORM_FAILED" and "*" 724 */ 725 private boolean verifyGenericErrorDetail(String errorDetailStr) { 726 boolean ret = false; 727 for (String str : GENERIC_ERROR_DETAIL_STRINGS) { 728 if (errorDetailStr.equals(str)) { 729 ret = true; 730 break; 731 } 732 } 733 return ret; 734 } 735 736 private @ErrorPolicyErrorType int getErrorPolicyErrorType(String errorType) { 737 return switch (errorType) { 738 case "IKE_PROTOCOL_ERROR_TYPE" -> IKE_PROTOCOL_ERROR_TYPE; 739 case "GENERIC_ERROR_TYPE" -> GENERIC_ERROR_TYPE; 740 case "*" -> FALLBACK_ERROR_TYPE; 741 default -> UNKNOWN_ERROR_TYPE; 742 }; 743 } 744 745 private synchronized Set<Integer> getAllUnthrottlingEvents() { 746 Set<Integer> events = new HashSet<>(); 747 for (Map.Entry<String, List<ErrorPolicy>> entry : mCarrierConfigPolicies.entrySet()) { 748 List<ErrorPolicy> errorPolicies = entry.getValue(); 749 for (ErrorPolicy errorPolicy : errorPolicies) { 750 events.addAll(errorPolicy.unthrottlingEvents()); 751 } 752 } 753 for (Map.Entry<String, List<ErrorPolicy>> entry : mDefaultPolicies.entrySet()) { 754 List<ErrorPolicy> errorPolicies = entry.getValue(); 755 for (ErrorPolicy errorPolicy : errorPolicies) { 756 events.addAll(errorPolicy.unthrottlingEvents()); 757 } 758 } 759 events.add(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT); 760 return events; 761 } 762 763 /** 764 * This method is called once on initialization of this class And is also called from handler on 765 * CARRIER_CONFIG_CHANGED event. There is no race condition between both as we register for the 766 * events after the calling this method. 767 */ 768 private synchronized void readFromCarrierConfig(int currentCarrierId) { 769 String carrierConfigErrorPolicy = 770 IwlanCarrierConfig.getConfigString( 771 mContext, mSlotId, IwlanCarrierConfig.KEY_ERROR_POLICY_CONFIG_STRING); 772 if (carrierConfigErrorPolicy == null) { 773 Log.e(LOG_TAG, "ErrorPolicy from Carrier Config is NULL"); 774 mCarrierConfigPolicies.clear(); 775 mCarrierConfigErrorPolicyString = null; 776 return; 777 } 778 try { 779 Map<String, List<ErrorPolicy>> errorPolicies = 780 readErrorPolicies(new JSONArray(carrierConfigErrorPolicy)); 781 if (!errorPolicies.isEmpty()) { 782 mCarrierConfigErrorPolicyString = carrierConfigErrorPolicy; 783 carrierId = currentCarrierId; 784 mCarrierConfigPolicies.clear(); 785 mCarrierConfigPolicies.putAll(errorPolicies); 786 } 787 } catch (JSONException | IllegalArgumentException e) { 788 Log.e( 789 LOG_TAG, 790 "Unable to parse the ErrorPolicy from CarrierConfig\n" 791 + carrierConfigErrorPolicy); 792 mCarrierConfigPolicies.clear(); 793 mCarrierConfigErrorPolicyString = null; 794 e.printStackTrace(); 795 } 796 } 797 798 private void updateUnthrottlingEvents() { 799 Set<Integer> registerEvents, unregisterEvents; 800 unregisterEvents = mUnthrottlingEvents; 801 registerEvents = getAllUnthrottlingEvents(); 802 mUnthrottlingEvents = getAllUnthrottlingEvents(); 803 804 if (unregisterEvents != null) { 805 registerEvents.removeAll(unregisterEvents); 806 unregisterEvents.removeAll(mUnthrottlingEvents); 807 } 808 809 IwlanEventListener.getInstance(mContext, mSlotId) 810 .addEventListener(new ArrayList<>(registerEvents), mHandler); 811 if (unregisterEvents != null) { 812 IwlanEventListener.getInstance(mContext, mSlotId) 813 .removeEventListener(new ArrayList<>(unregisterEvents), mHandler); 814 } 815 Log.d( 816 LOG_TAG, 817 "UnthrottlingEvents: " 818 + (mUnthrottlingEvents != null 819 ? Arrays.toString(mUnthrottlingEvents.toArray()) 820 : "null")); 821 } 822 823 private synchronized void unthrottleLastErrorOnEvent(int event) { 824 Log.d(LOG_TAG, "unthrottleLastErrorOnEvent: " + event); 825 // Pass the other events to RetryActionStore to check if can unthrottle 826 mRetryActionStoreByApn.forEach( 827 (apn, retryActionStore) -> retryActionStore.handleUnthrottlingEvent(event)); 828 // Carrier Config Changed should clear all RetryActionStore 829 if (event == IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT) { 830 mRetryActionStoreByApn.clear(); 831 } 832 } 833 834 @VisibleForTesting 835 ErrorStats getErrorStats() { 836 return mErrorStats; 837 } 838 839 @AutoValue 840 abstract static class ErrorPolicy { 841 private static final String LOG_TAG = ErrorPolicyManager.class.getSimpleName(); 842 843 abstract @ErrorPolicyErrorType int errorType(); 844 845 abstract List<String> errorDetails(); 846 847 abstract List<Integer> retryArray(); 848 849 abstract boolean infiniteRetriesWithLastRetryTime(); 850 851 abstract List<Integer> unthrottlingEvents(); 852 853 abstract Optional<Integer> numAttemptsPerFqdn(); 854 855 abstract Optional<Integer> handoverAttemptCount(); 856 857 @AutoValue.Builder 858 abstract static class Builder { 859 abstract Builder setErrorType(int errorType); 860 861 abstract Builder setErrorDetails(List<String> errorDetails); 862 863 abstract Builder setRetryArray(List<Integer> retryArray); 864 865 abstract Builder setInfiniteRetriesWithLastRetryTime( 866 boolean infiniteRetriesWithLastRetryTime); 867 868 abstract Builder setUnthrottlingEvents(List<Integer> unthrottlingEvents); 869 870 abstract Builder setNumAttemptsPerFqdn(Integer numAttemptsPerFqdn); 871 872 abstract Builder setHandoverAttemptCount(Integer handoverAttemptCount); 873 874 abstract ErrorPolicy build(); 875 } 876 877 long getRetryTime(int index) { 878 long retryTime = -1; 879 if (retryArray().size() > 0) { 880 // If the index is greater than or equal to the last element's index 881 // and if the last item in the retryArray is "-1" use the retryTime 882 // of the element before the last element to repeat the element. 883 if (infiniteRetriesWithLastRetryTime()) { 884 index = Math.min(index, retryArray().size() - 2); 885 } 886 if (index >= 0 && index < retryArray().size()) { 887 retryTime = retryArray().get(index); 888 } 889 } 890 891 // retryTime -1 represents indefinite failure. In that case 892 // return time that represents 1 day to not retry for that day. 893 if (retryTime == -1L) { 894 retryTime = TimeUnit.DAYS.toSeconds(1); 895 } 896 return retryTime; 897 } 898 899 int getCurrentFqdnIndex(int retryIndex, int numFqdns) { 900 int result = -1; 901 if (numAttemptsPerFqdn().isEmpty() || retryArray().size() <= 0) { 902 return result; 903 } 904 // Cycles between 0 and (numFqdns - 1), based on the current attempt count and size of 905 // mRetryArray. 906 return (retryIndex + 1) / numAttemptsPerFqdn().get() % numFqdns; 907 } 908 909 @ErrorPolicyErrorType 910 int getErrorType() { 911 return errorType(); 912 } 913 914 int getHandoverAttemptCount() { 915 return handoverAttemptCount().orElse(Integer.MAX_VALUE); 916 } 917 918 synchronized boolean canUnthrottle(int event) { 919 return unthrottlingEvents().contains(event); 920 } 921 922 boolean match(IwlanError iwlanError) { 923 // Generic by default to match to generic policy. 924 String iwlanErrorDetail; 925 if (errorType() == FALLBACK_ERROR_TYPE) { 926 return true; 927 } else if (errorType() == IKE_PROTOCOL_ERROR_TYPE 928 && iwlanError.getErrorType() == IwlanError.IKE_PROTOCOL_EXCEPTION) { 929 IkeProtocolException exception = (IkeProtocolException) iwlanError.getException(); 930 iwlanErrorDetail = String.valueOf(exception.getErrorType()); 931 } else if (errorType() == GENERIC_ERROR_TYPE) { 932 iwlanErrorDetail = getGenericErrorDetailString(iwlanError); 933 if (iwlanErrorDetail.equals("UNKNOWN")) { 934 return false; 935 } 936 } else { 937 return false; 938 } 939 940 boolean ret = false; 941 for (String errorDetail : errorDetails()) { 942 if (errorType() == IKE_PROTOCOL_ERROR_TYPE 943 && iwlanError.getErrorType() == IwlanError.IKE_PROTOCOL_EXCEPTION 944 && errorDetail.contains("-")) { 945 // error detail is stored in range format. 946 // ErrorPolicyManager#verifyIkeProtocolErrorDetail will make sure that 947 // this is stored correctly in "min-max" format. 948 String[] range = errorDetail.split("-"); 949 int min = Integer.parseInt(range[0]); 950 int max = Integer.parseInt(range[1]); 951 int error = Integer.parseInt(iwlanErrorDetail); 952 if (error >= min && error <= max) { 953 ret = true; 954 break; 955 } 956 } else if (errorDetail.equals(iwlanErrorDetail) || errorDetail.equals("*")) { 957 ret = true; 958 break; 959 } 960 } 961 return ret; 962 } 963 964 void log() { 965 Log.d(LOG_TAG, "ErrorType: " + errorType()); 966 Log.d(LOG_TAG, "ErrorDetail: " + Arrays.toString(errorDetails().toArray())); 967 Log.d(LOG_TAG, "RetryArray: " + Arrays.toString(retryArray().toArray())); 968 Log.d( 969 LOG_TAG, 970 "InfiniteRetriesWithLastRetryTime: " + infiniteRetriesWithLastRetryTime()); 971 Log.d( 972 LOG_TAG, 973 "UnthrottlingEvents: " + Arrays.toString(unthrottlingEvents().toArray())); 974 Log.d(LOG_TAG, "NumAttemptsPerFqdn: " + numAttemptsPerFqdn()); 975 Log.d(LOG_TAG, "handoverAttemptCount: " + handoverAttemptCount()); 976 } 977 978 boolean isFallback() { 979 return (errorType() == FALLBACK_ERROR_TYPE) 980 || (errorDetails().size() == 1 && errorDetails().get(0).equals("*")); 981 } 982 983 String getGenericErrorDetailString(IwlanError iwlanError) { 984 return switch (iwlanError.getErrorType()) { 985 case IwlanError.IKE_INTERNAL_IO_EXCEPTION -> "IO_EXCEPTION"; 986 case IwlanError.EPDG_SELECTOR_SERVER_SELECTION_FAILED -> "SERVER_SELECTION_FAILED"; 987 case IwlanError.TUNNEL_TRANSFORM_FAILED -> "TUNNEL_TRANSFORM_FAILED"; 988 case IwlanError.IKE_NETWORK_LOST_EXCEPTION -> "IKE_NETWORK_LOST_EXCEPTION"; 989 case IwlanError.EPDG_ADDRESS_ONLY_IPV4_ALLOWED -> "EPDG_ADDRESS_ONLY_IPV4_ALLOWED"; 990 case IwlanError.EPDG_ADDRESS_ONLY_IPV6_ALLOWED -> "EPDG_ADDRESS_ONLY_IPV6_ALLOWED"; 991 // TODO: Add TIMEOUT_EXCEPTION processing 992 // TODO: Add all the missing error detail string 993 case IwlanError.IKE_INIT_TIMEOUT -> "IKE_INIT_TIMEOUT"; 994 case IwlanError.IKE_MOBILITY_TIMEOUT -> "IKE_MOBILITY_TIMEOUT"; 995 case IwlanError.IKE_DPD_TIMEOUT -> "IKE_DPD_TIMEOUT"; 996 default -> "UNKNOWN"; 997 }; 998 } 999 } 1000 1001 /** 1002 * A data class to store the error cause and the applied error policy. This class is responsible 1003 * to calculate the retry time base on the error policy / config. 1004 */ 1005 interface RetryAction { 1006 IwlanError error(); 1007 1008 ErrorPolicy errorPolicy(); 1009 1010 long lastErrorTime(); 1011 1012 /** The total time should be waited between lastErrorTime and next retry. */ 1013 Duration totalBackoffDuration(); 1014 1015 /** The number of same cause error observed since last success / unthrottle event. */ 1016 int errorCountOfSameCause(); 1017 1018 boolean shouldRetryWithInitialAttach(); 1019 1020 int getCurrentFqdnIndex(int numFqdns); 1021 } 1022 1023 /** RetryAction with retry time defined by retry index and error policy */ 1024 record PolicyDerivedRetryAction( 1025 @Override IwlanError error, 1026 @Override ErrorPolicy errorPolicy, 1027 @Override long lastErrorTime, 1028 @Override int errorCountOfSameCause, 1029 int currentRetryIndex) 1030 implements RetryAction { 1031 @Override 1032 public Duration totalBackoffDuration() { 1033 return Duration.ofSeconds(errorPolicy().getRetryTime(currentRetryIndex())); 1034 } 1035 1036 @Override 1037 public int getCurrentFqdnIndex(int numFqdns) { 1038 ErrorPolicy errorPolicy = errorPolicy(); 1039 return errorPolicy.getCurrentFqdnIndex(currentRetryIndex(), numFqdns); 1040 } 1041 1042 @Override 1043 public boolean shouldRetryWithInitialAttach() { 1044 // UE should only uses initial attach to reset network failure, not for UE internal or 1045 // DNS errors. When the number of handover failures due to network issues exceeds the 1046 // configured threshold, UE should request network with initial attach instead of 1047 // handover request. 1048 ErrorPolicy errorPolicy = errorPolicy(); 1049 return errorPolicy.getErrorType() == IKE_PROTOCOL_ERROR_TYPE 1050 && currentRetryIndex() + 1 >= errorPolicy.getHandoverAttemptCount(); 1051 } 1052 } 1053 1054 /** RetryAction with retry time defined by backoff time in tunnel config */ 1055 record IkeBackoffNotifyRetryAction( 1056 @Override IwlanError error, 1057 @Override ErrorPolicy errorPolicy, 1058 @Override long lastErrorTime, 1059 @Override int errorCountOfSameCause, 1060 @Override Duration totalBackoffDuration) 1061 implements RetryAction { 1062 1063 @Override 1064 public int getCurrentFqdnIndex(int numFqdns) { 1065 // Not applicable for backoff time configured case, therefore returning 0 here 1066 return 0; 1067 } 1068 1069 @Override 1070 public boolean shouldRetryWithInitialAttach() { 1071 // TODO(b/308745683): Initial attach condition is undefined for backoff config case 1072 ErrorPolicy errorPolicy = errorPolicy(); 1073 return errorPolicy.getErrorType() == IKE_PROTOCOL_ERROR_TYPE 1074 && errorPolicy.getHandoverAttemptCount() == 0; 1075 } 1076 } 1077 1078 interface ErrorCause { 1079 @IwlanError.IwlanErrorType 1080 int iwlanErrorType(); 1081 1082 static ErrorCause fromIwlanError(IwlanError iwlanError) { 1083 if (iwlanError.getErrorType() == IwlanError.IKE_PROTOCOL_EXCEPTION) { 1084 return new AutoValue_ErrorPolicyManager_IkeProtocolErrorCause( 1085 /* ikeProtocolErrorType= */ ((IkeProtocolException) 1086 iwlanError.getException()) 1087 .getErrorType()); 1088 } 1089 return new AutoValue_ErrorPolicyManager_NonIkeProtocolErrorCause( 1090 /* iwlanErrorType= */ iwlanError.getErrorType()); 1091 } 1092 } 1093 1094 @AutoValue 1095 abstract static class NonIkeProtocolErrorCause implements ErrorCause {} 1096 1097 /** 1098 * An IkeProtocolErrorCause will carry the ike protocol error type, so that different protocol 1099 * error will be treated as different error cause 1100 */ 1101 @AutoValue 1102 abstract static class IkeProtocolErrorCause implements ErrorCause { 1103 @Override 1104 @IwlanError.IwlanErrorType 1105 public int iwlanErrorType() { 1106 return IwlanError.IKE_PROTOCOL_EXCEPTION; 1107 } 1108 1109 // @IkeProtocolException.ErrorType is hidden API 1110 abstract int ikeProtocolErrorType(); 1111 } 1112 1113 /** 1114 * This class manage and store the RetryAction of the APN, and responsible to create RetryAction 1115 * when IwlanError received. 1116 */ 1117 class ApnRetryActionStore { 1118 final String mApn; 1119 final ConcurrentHashMap<ErrorCause, RetryAction> mLastRetryActionByCause; 1120 @Nullable RetryAction mLastRetryAction; 1121 1122 ApnRetryActionStore(String apn) { 1123 mApn = apn; 1124 mLastRetryActionByCause = new ConcurrentHashMap<>(); 1125 } 1126 1127 /** 1128 * Determines whether the new {@link RetryAction} should accumulate the retry index from 1129 * {@code prevRetryAction}. 1130 * 1131 * @param prevRetryAction the previous RetryAction (can be null). 1132 * @param newIwlanError the new IwlanError. 1133 * @return true if {@code prevRetryAction} is an instance of {@link 1134 * PolicyDerivedRetryAction} and is the same {@link ErrorCause} as {@code 1135 * newIwlanError}, false otherwise. 1136 */ 1137 private boolean shouldAccumulateRetryIndex( 1138 @Nullable RetryAction prevRetryAction, IwlanError newIwlanError) { 1139 if (!(prevRetryAction instanceof PolicyDerivedRetryAction)) { 1140 return false; 1141 } 1142 1143 boolean isSameIwlanError = prevRetryAction.error().equals(newIwlanError); 1144 // If prev and current error are both IKE_PROTOCOL_EXCEPTION, keep the retry index 1145 // TODO: b/292312000 - Workaround for RetryIndex lost 1146 boolean areBothIkeProtocolException = 1147 (newIwlanError.getErrorType() == IwlanError.IKE_PROTOCOL_EXCEPTION 1148 && prevRetryAction.error().getErrorType() 1149 == IwlanError.IKE_PROTOCOL_EXCEPTION); 1150 boolean shouldAccumulateRetryIndex = isSameIwlanError || areBothIkeProtocolException; 1151 1152 if (!shouldAccumulateRetryIndex) { 1153 Log.d(LOG_TAG, "Doesn't match to the previous error" + newIwlanError); 1154 } 1155 1156 return shouldAccumulateRetryIndex; 1157 } 1158 1159 private PolicyDerivedRetryAction generateRetryAction(IwlanError iwlanError) { 1160 ErrorCause errorCause = ErrorCause.fromIwlanError(iwlanError); 1161 1162 @Nullable RetryAction prevRetryAction = mLastRetryActionByCause.get(errorCause); 1163 int newErrorCount = 1164 prevRetryAction != null ? prevRetryAction.errorCountOfSameCause() + 1 : 1; 1165 boolean shouldAccumulateRetryIndex = 1166 shouldAccumulateRetryIndex(prevRetryAction, iwlanError); 1167 int newRetryIndex = 1168 shouldAccumulateRetryIndex 1169 ? ((PolicyDerivedRetryAction) prevRetryAction).currentRetryIndex() + 1 1170 : 0; 1171 1172 ErrorPolicy policy = findErrorPolicy(mApn, iwlanError); 1173 PolicyDerivedRetryAction newRetryAction = 1174 new PolicyDerivedRetryAction( 1175 iwlanError, 1176 policy, 1177 IwlanHelper.elapsedRealtime(), 1178 newErrorCount, 1179 newRetryIndex); 1180 mLastRetryActionByCause.put(errorCause, newRetryAction); 1181 mLastRetryAction = newRetryAction; 1182 1183 return newRetryAction; 1184 } 1185 1186 private IkeBackoffNotifyRetryAction generateRetryAction( 1187 IwlanError iwlanError, Duration backoffDuration) { 1188 ErrorCause errorCause = ErrorCause.fromIwlanError(iwlanError); 1189 @Nullable RetryAction prevRetryAction = mLastRetryActionByCause.get(errorCause); 1190 int newErrorCount = 1191 prevRetryAction != null ? prevRetryAction.errorCountOfSameCause() + 1 : 1; 1192 ErrorPolicy policy = findErrorPolicy(mApn, iwlanError); 1193 // For configured back off time case, simply create new RetryAction, nothing need to 1194 // keep 1195 IkeBackoffNotifyRetryAction newRetryAction = 1196 new IkeBackoffNotifyRetryAction( 1197 iwlanError, 1198 policy, 1199 IwlanHelper.elapsedRealtime(), 1200 newErrorCount, 1201 backoffDuration); 1202 mLastRetryActionByCause.put(errorCause, newRetryAction); 1203 mLastRetryAction = newRetryAction; 1204 1205 return newRetryAction; 1206 } 1207 1208 /** 1209 * Set {@code lastRetryAction} to null if {@code lastRetryAction} can be unthrottled by the 1210 * event. Clear those reserved retry index and the {@link RetryAction} if any {@link 1211 * RetryAction} in {@code mLastRetryActionByCause} can be unthrottled by the event. 1212 * 1213 * @param event the handling event 1214 */ 1215 private void handleUnthrottlingEvent(int event) { 1216 if (event == IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT) { 1217 mLastRetryActionByCause.clear(); 1218 } else { 1219 // Check all stored RetryAction, remove from the store if it can be unthrottle. 1220 // By removing it, the retry index (for PolicyDerived) will reset as 0 1221 mLastRetryActionByCause 1222 .entrySet() 1223 .removeIf(it -> it.getValue().errorPolicy().canUnthrottle(event)); 1224 } 1225 1226 DataService.DataServiceProvider provider = 1227 IwlanDataService.getDataServiceProvider(mSlotId); 1228 1229 boolean isCarrierConfigChanged = 1230 event == IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT; 1231 boolean isLastRetryActionCanUnthrottle = 1232 mLastRetryAction != null && mLastRetryAction.errorPolicy().canUnthrottle(event); 1233 if (isCarrierConfigChanged || isLastRetryActionCanUnthrottle) { 1234 mLastRetryAction = null; 1235 1236 if (provider == null) { 1237 Log.w(LOG_TAG, "DataServiceProvider not found for slot: " + mSlotId); 1238 } else { 1239 provider.notifyApnUnthrottled(mApn); 1240 Log.d(LOG_TAG, "unthrottled error for: " + mApn); 1241 } 1242 } 1243 } 1244 1245 @Nullable 1246 private RetryAction getLastRetryAction() { 1247 return mLastRetryAction; 1248 } 1249 } 1250 1251 static class ApnWithIwlanError { 1252 @NonNull final String mApn; 1253 @NonNull final IwlanError mIwlanError; 1254 1255 ApnWithIwlanError(@NonNull String apn, @NonNull IwlanError iwlanError) { 1256 mApn = apn; 1257 mIwlanError = iwlanError; 1258 } 1259 } 1260 1261 private boolean isValidCarrierConfigChangedEvent(int currentCarrierId) { 1262 String errorPolicyConfig = 1263 IwlanCarrierConfig.getConfigString( 1264 mContext, mSlotId, IwlanCarrierConfig.KEY_ERROR_POLICY_CONFIG_STRING); 1265 return (currentCarrierId != carrierId) 1266 || (mCarrierConfigErrorPolicyString == null) 1267 || (errorPolicyConfig != null 1268 && !Objects.equals(mCarrierConfigErrorPolicyString, errorPolicyConfig)); 1269 } 1270 1271 private final class EpmHandler extends Handler { 1272 private final String TAG = EpmHandler.class.getSimpleName(); 1273 1274 @Override 1275 public void handleMessage(Message msg) { 1276 Log.d(TAG, "msg.what = " + msg.what); 1277 switch (msg.what) { 1278 case IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT: 1279 Log.d(TAG, "On CARRIER_CONFIG_CHANGED_EVENT"); 1280 int currentCarrierId = IwlanHelper.getCarrierId(mContext, mSlotId); 1281 if (isValidCarrierConfigChangedEvent(currentCarrierId)) { 1282 Log.d(TAG, "Unthrottle last error and read from carrier config"); 1283 unthrottleLastErrorOnEvent(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT); 1284 readFromCarrierConfig(currentCarrierId); 1285 updateUnthrottlingEvents(); 1286 } 1287 break; 1288 case IwlanEventListener.APM_ENABLE_EVENT: 1289 case IwlanEventListener.APM_DISABLE_EVENT: 1290 case IwlanEventListener.WIFI_DISABLE_EVENT: 1291 case IwlanEventListener.WIFI_CALLING_DISABLE_EVENT: 1292 case IwlanEventListener.WIFI_AP_CHANGED_EVENT: 1293 unthrottleLastErrorOnEvent(msg.what); 1294 break; 1295 default: 1296 Log.d(TAG, "Unknown message received!"); 1297 break; 1298 } 1299 } 1300 1301 EpmHandler(Looper looper) { 1302 super(looper); 1303 } 1304 } 1305 1306 @VisibleForTesting 1307 static class ErrorStats { 1308 @VisibleForTesting Map<String, Map<String, Long>> mStats = new HashMap<>(); 1309 private Date mStartTime; 1310 private int mStatCount; 1311 private static final int APN_COUNT_MAX = 10; 1312 private static final int ERROR_COUNT_MAX = 1000; 1313 1314 ErrorStats() { 1315 mStartTime = Calendar.getInstance().getTime(); 1316 mStatCount = 0; 1317 } 1318 1319 void update(String apn, IwlanError error) { 1320 if (mStats.size() >= APN_COUNT_MAX || mStatCount >= ERROR_COUNT_MAX) { 1321 reset(); 1322 } 1323 if (!mStats.containsKey(apn)) { 1324 mStats.put(apn, new HashMap<>()); 1325 } 1326 Map<String, Long> errorMap = mStats.get(apn); 1327 String errorString = error.toString(); 1328 if (!errorMap.containsKey(errorString)) { 1329 errorMap.put(errorString, 0L); 1330 } 1331 long count = errorMap.get(errorString); 1332 errorMap.put(errorString, ++count); 1333 mStats.put(apn, errorMap); 1334 mStatCount++; 1335 } 1336 1337 void reset() { 1338 mStartTime = Calendar.getInstance().getTime(); 1339 mStats = new HashMap<>(); 1340 mStatCount = 0; 1341 } 1342 1343 @Override 1344 public String toString() { 1345 StringBuilder sb = new StringBuilder(); 1346 sb.append("mStartTime: ").append(mStartTime); 1347 sb.append("\nErrorStats"); 1348 for (Map.Entry<String, Map<String, Long>> entry : mStats.entrySet()) { 1349 sb.append("\n\tApn: ").append(entry.getKey()); 1350 for (Map.Entry<String, Long> errorEntry : entry.getValue().entrySet()) { 1351 sb.append("\n\t ") 1352 .append(errorEntry.getKey()) 1353 .append(" : ") 1354 .append(errorEntry.getValue()); 1355 } 1356 } 1357 return sb.toString(); 1358 } 1359 } 1360 } 1361