1 /* 2 * Copyright 2021 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.android.internal.telephony.data; 18 19 import android.annotation.CallbackExecutor; 20 import android.annotation.ElapsedRealtimeLong; 21 import android.annotation.IntDef; 22 import android.annotation.NonNull; 23 import android.content.Intent; 24 import android.net.NetworkAgent; 25 import android.os.Handler; 26 import android.os.Looper; 27 import android.os.Message; 28 import android.os.SystemClock; 29 import android.provider.Settings; 30 import android.telephony.Annotation.RadioPowerState; 31 import android.telephony.Annotation.ValidationStatus; 32 import android.telephony.CellSignalStrength; 33 import android.telephony.SubscriptionManager; 34 import android.telephony.TelephonyManager; 35 import android.telephony.data.DataProfile; 36 import android.util.IndentingPrintWriter; 37 import android.util.LocalLog; 38 39 import com.android.internal.annotations.VisibleForTesting; 40 import com.android.internal.telephony.Phone; 41 import com.android.internal.telephony.PhoneConstants; 42 import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback; 43 import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback; 44 import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback; 45 import com.android.internal.telephony.metrics.DataStallRecoveryStats; 46 import com.android.internal.telephony.metrics.TelephonyMetrics; 47 import com.android.telephony.Rlog; 48 49 import java.io.FileDescriptor; 50 import java.io.PrintWriter; 51 import java.lang.annotation.Retention; 52 import java.lang.annotation.RetentionPolicy; 53 import java.util.Arrays; 54 import java.util.List; 55 import java.util.concurrent.Executor; 56 57 /** 58 * DataStallRecoveryManager monitors the network validation result from connectivity service and 59 * takes actions to recovery data network. 60 */ 61 public class DataStallRecoveryManager extends Handler { 62 private static final boolean VDBG = false; 63 64 /** Recovery actions taken in case of data stall */ 65 @IntDef( 66 value = { 67 RECOVERY_ACTION_GET_DATA_CALL_LIST, 68 RECOVERY_ACTION_CLEANUP, 69 RECOVERY_ACTION_REREGISTER, 70 RECOVERY_ACTION_RADIO_RESTART, 71 RECOVERY_ACTION_RESET_MODEM 72 }) 73 @Retention(RetentionPolicy.SOURCE) 74 public @interface RecoveryAction {}; 75 76 /* DataStallRecoveryManager queries RIL for link properties (IP addresses, DNS server addresses 77 * etc) using RIL_REQUEST_GET_DATA_CALL_LIST. This will help in cases where the data stall 78 * occurred because of a link property changed but not notified to connectivity service. 79 */ 80 public static final int RECOVERY_ACTION_GET_DATA_CALL_LIST = 0; 81 82 /* DataStallRecoveryManager will request DataNetworkController to reestablish internet using 83 * RIL_REQUEST_DEACTIVATE_DATA_CALL and sets up the data call back using SETUP_DATA_CALL. 84 * It will help to reestablish the channel between RIL and modem. 85 */ 86 public static final int RECOVERY_ACTION_CLEANUP = 1; 87 88 /** 89 * Add the RECOVERY_ACTION_REREGISTER to align the RecoveryActions between 90 * DataStallRecoveryManager and atoms.proto. In Android T, This action will not process because 91 * the boolean array for skip recovery action is default true in carrier config setting. 92 * 93 * @deprecated Do not use. 94 */ 95 @java.lang.Deprecated 96 public static final int RECOVERY_ACTION_REREGISTER = 2; 97 98 /* DataStallRecoveryManager will request ServiceStateTracker to send RIL_REQUEST_RADIO_POWER 99 * to restart radio. It will restart the radio and re-attch to the network. 100 */ 101 public static final int RECOVERY_ACTION_RADIO_RESTART = 3; 102 103 /* DataStallRecoveryManager will request to reboot modem using NV_RESET_CONFIG. It will recover 104 * if there is a problem in modem side. 105 */ 106 public static final int RECOVERY_ACTION_RESET_MODEM = 4; 107 108 /** Recovered reason taken in case of data stall recovered */ 109 @IntDef( 110 value = { 111 RECOVERED_REASON_NONE, 112 RECOVERED_REASON_DSRM, 113 RECOVERED_REASON_MODEM, 114 RECOVERED_REASON_USER 115 }) 116 @Retention(RetentionPolicy.SOURCE) 117 public @interface RecoveredReason {}; 118 119 /** The reason when data stall recovered. */ 120 /** The data stall not recovered yet. */ 121 private static final int RECOVERED_REASON_NONE = 0; 122 /** The data stall recovered by our DataStallRecoveryManager. */ 123 private static final int RECOVERED_REASON_DSRM = 1; 124 /** The data stall recovered by modem(Radio Power off/on). */ 125 private static final int RECOVERED_REASON_MODEM = 2; 126 /** The data stall recovered by user (Mobile Data Power off/on). */ 127 private static final int RECOVERED_REASON_USER = 3; 128 129 /** Event for triggering recovery action. */ 130 private static final int EVENT_DO_RECOVERY = 2; 131 132 /** Event for radio state changed. */ 133 private static final int EVENT_RADIO_STATE_CHANGED = 3; 134 135 private final @NonNull Phone mPhone; 136 private final @NonNull String mLogTag; 137 private final @NonNull LocalLog mLocalLog = new LocalLog(128); 138 139 /** Data network controller */ 140 private final @NonNull DataNetworkController mDataNetworkController; 141 142 /** Data config manager */ 143 private final @NonNull DataConfigManager mDataConfigManager; 144 145 /** Cellular data service */ 146 private final @NonNull DataServiceManager mWwanDataServiceManager; 147 148 /** The data stall recovery action. */ 149 private @RecoveryAction int mRecovryAction; 150 /** The elapsed real time of last recovery attempted */ 151 private @ElapsedRealtimeLong long mTimeLastRecoveryStartMs; 152 /** Whether current network is good or not */ 153 private boolean mIsValidNetwork; 154 /** Whether data stall recovery is triggered or not */ 155 private boolean mRecoveryTriggered = false; 156 /** Whether data stall happened or not. */ 157 private boolean mDataStalled; 158 /** Whether the result of last action(RADIO_RESTART) reported. */ 159 private boolean mLastActionReported; 160 /** The real time for data stall start. */ 161 @VisibleForTesting 162 public @ElapsedRealtimeLong long mDataStallStartMs; 163 /** Last data stall recovery action. */ 164 private @RecoveryAction int mLastAction; 165 /** Last radio power state. */ 166 private @RadioPowerState int mRadioPowerState; 167 /** Whether the NetworkCheckTimer start. */ 168 private boolean mNetworkCheckTimerStarted = false; 169 /** Whether radio state changed during data stall. */ 170 private boolean mRadioStateChangedDuringDataStall; 171 /** Whether airplane mode enabled during data stall. */ 172 private boolean mIsAirPlaneModeEnableDuringDataStall; 173 /** Whether mobile data change to Enabled during data stall. */ 174 private boolean mMobileDataChangedToEnabledDuringDataStall; 175 /** Whether attempted all recovery steps. */ 176 private boolean mIsAttemptedAllSteps; 177 /** Whether internet network connected. */ 178 private boolean mIsInternetNetworkConnected; 179 /** The durations for current recovery action */ 180 private @ElapsedRealtimeLong long mTimeElapsedOfCurrentAction; 181 182 /** The array for the timers between recovery actions. */ 183 private @NonNull long[] mDataStallRecoveryDelayMillisArray; 184 /** The boolean array for the flags. They are used to skip the recovery actions if needed. */ 185 private @NonNull boolean[] mSkipRecoveryActionArray; 186 187 private DataStallRecoveryManagerCallback mDataStallRecoveryManagerCallback; 188 189 /** 190 * The data stall recovery manager callback. Note this is only used for passing information 191 * internally in the data stack, should not be used externally. 192 */ 193 public abstract static class DataStallRecoveryManagerCallback extends DataCallback { 194 /** 195 * Constructor 196 * 197 * @param executor The executor of the callback. 198 */ DataStallRecoveryManagerCallback(@onNull @allbackExecutor Executor executor)199 public DataStallRecoveryManagerCallback(@NonNull @CallbackExecutor Executor executor) { 200 super(executor); 201 } 202 203 /** 204 * Called when data stall occurs and needed to tear down / setup a new data network for 205 * internet. 206 */ onDataStallReestablishInternet()207 public abstract void onDataStallReestablishInternet(); 208 } 209 210 /** 211 * Constructor 212 * 213 * @param phone The phone instance. 214 * @param dataNetworkController Data network controller 215 * @param dataServiceManager The WWAN data service manager. 216 * @param looper The looper to be used by the handler. Currently the handler thread is the phone 217 * process's main thread. 218 * @param callback Callback to notify data network controller for data stall events. 219 */ DataStallRecoveryManager( @onNull Phone phone, @NonNull DataNetworkController dataNetworkController, @NonNull DataServiceManager dataServiceManager, @NonNull Looper looper, @NonNull DataStallRecoveryManagerCallback callback)220 public DataStallRecoveryManager( 221 @NonNull Phone phone, 222 @NonNull DataNetworkController dataNetworkController, 223 @NonNull DataServiceManager dataServiceManager, 224 @NonNull Looper looper, 225 @NonNull DataStallRecoveryManagerCallback callback) { 226 super(looper); 227 mPhone = phone; 228 mLogTag = "DSRM-" + mPhone.getPhoneId(); 229 log("DataStallRecoveryManager created."); 230 mDataNetworkController = dataNetworkController; 231 mWwanDataServiceManager = dataServiceManager; 232 mDataConfigManager = mDataNetworkController.getDataConfigManager(); 233 mDataNetworkController 234 .getDataSettingsManager() 235 .registerCallback( 236 new DataSettingsManagerCallback(this::post) { 237 @Override 238 public void onDataEnabledChanged( 239 boolean enabled, 240 @TelephonyManager.DataEnabledChangedReason int reason, 241 @NonNull String callingPackage) { 242 onMobileDataEnabledChanged(enabled); 243 } 244 }); 245 mDataStallRecoveryManagerCallback = callback; 246 mRadioPowerState = mPhone.getRadioPowerState(); 247 updateDataStallRecoveryConfigs(); 248 249 registerAllEvents(); 250 } 251 252 /** Register for all events that data stall monitor is interested. */ registerAllEvents()253 private void registerAllEvents() { 254 mDataConfigManager.registerCallback(new DataConfigManagerCallback(this::post) { 255 @Override 256 public void onCarrierConfigChanged() { 257 DataStallRecoveryManager.this.onCarrierConfigUpdated(); 258 } 259 }); 260 mDataNetworkController.registerDataNetworkControllerCallback( 261 new DataNetworkControllerCallback(this::post) { 262 @Override 263 public void onInternetDataNetworkValidationStatusChanged( 264 @ValidationStatus int validationStatus) { 265 onInternetValidationStatusChanged(validationStatus); 266 } 267 268 @Override 269 public void onInternetDataNetworkConnected( 270 @NonNull List<DataProfile> dataProfiles) { 271 mIsInternetNetworkConnected = true; 272 logl("onInternetDataNetworkConnected"); 273 } 274 275 @Override 276 public void onInternetDataNetworkDisconnected() { 277 mIsInternetNetworkConnected = false; 278 logl("onInternetDataNetworkDisconnected"); 279 } 280 }); 281 mPhone.mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null); 282 } 283 284 @Override handleMessage(Message msg)285 public void handleMessage(Message msg) { 286 logv("handleMessage = " + msg); 287 switch (msg.what) { 288 case EVENT_DO_RECOVERY: 289 doRecovery(); 290 break; 291 case EVENT_RADIO_STATE_CHANGED: 292 mRadioPowerState = mPhone.getRadioPowerState(); 293 if (mDataStalled) { 294 // Store the radio state changed flag only when data stall occurred. 295 mRadioStateChangedDuringDataStall = true; 296 if (Settings.Global.getInt( 297 mPhone.getContext().getContentResolver(), 298 Settings.Global.AIRPLANE_MODE_ON, 299 0) != 0) { 300 mIsAirPlaneModeEnableDuringDataStall = true; 301 } 302 } 303 break; 304 default: 305 loge("Unexpected message = " + msg); 306 break; 307 } 308 } 309 310 /** Update the data stall recovery configs from DataConfigManager. */ updateDataStallRecoveryConfigs()311 private void updateDataStallRecoveryConfigs() { 312 mDataStallRecoveryDelayMillisArray = mDataConfigManager.getDataStallRecoveryDelayMillis(); 313 mSkipRecoveryActionArray = mDataConfigManager.getDataStallRecoveryShouldSkipArray(); 314 } 315 316 /** 317 * Get the duration for specific data stall recovery action. 318 * 319 * @param recoveryAction The recovery action to query. 320 * @return the delay in milliseconds for the specific recovery action. 321 */ getDataStallRecoveryDelayMillis(@ecoveryAction int recoveryAction)322 private long getDataStallRecoveryDelayMillis(@RecoveryAction int recoveryAction) { 323 return mDataStallRecoveryDelayMillisArray[recoveryAction]; 324 } 325 326 /** 327 * Check if the recovery action needs to be skipped. 328 * 329 * @param recoveryAction The recovery action. 330 * @return {@code true} if the action needs to be skipped. 331 */ shouldSkipRecoveryAction(@ecoveryAction int recoveryAction)332 private boolean shouldSkipRecoveryAction(@RecoveryAction int recoveryAction) { 333 return mSkipRecoveryActionArray[recoveryAction]; 334 } 335 336 /** Called when carrier config was updated. */ onCarrierConfigUpdated()337 private void onCarrierConfigUpdated() { 338 updateDataStallRecoveryConfigs(); 339 } 340 341 /** 342 * Called when mobile data setting changed. 343 * 344 * @param enabled true for mobile data settings enabled & false for disabled. 345 */ onMobileDataEnabledChanged(boolean enabled)346 private void onMobileDataEnabledChanged(boolean enabled) { 347 logl("onMobileDataEnabledChanged: DataEnabled:" + enabled + ",DataStalled:" + mDataStalled); 348 // Store the mobile data changed flag (from disabled to enabled) as TRUE 349 // during data stalled. 350 if (mDataStalled && enabled) { 351 mMobileDataChangedToEnabledDuringDataStall = true; 352 } 353 } 354 355 /** 356 * Called when internet validation status passed. We will initialize all parameters. 357 */ reset()358 private void reset() { 359 mIsValidNetwork = true; 360 mRecoveryTriggered = false; 361 mIsAttemptedAllSteps = false; 362 mRadioStateChangedDuringDataStall = false; 363 mIsAirPlaneModeEnableDuringDataStall = false; 364 mMobileDataChangedToEnabledDuringDataStall = false; 365 cancelNetworkCheckTimer(); 366 mTimeLastRecoveryStartMs = 0; 367 mLastAction = RECOVERY_ACTION_GET_DATA_CALL_LIST; 368 mRecovryAction = RECOVERY_ACTION_GET_DATA_CALL_LIST; 369 } 370 371 /** 372 * Called when internet validation status changed. 373 * 374 * @param status Validation status. 375 */ onInternetValidationStatusChanged(@alidationStatus int status)376 private void onInternetValidationStatusChanged(@ValidationStatus int status) { 377 logl("onInternetValidationStatusChanged: " + DataUtils.validationStatusToString(status)); 378 final boolean isValid = status == NetworkAgent.VALIDATION_STATUS_VALID; 379 setNetworkValidationState(isValid); 380 if (isValid) { 381 reset(); 382 } else if (isRecoveryNeeded(true)) { 383 // Set the network as invalid, because recovery is needed 384 mIsValidNetwork = false; 385 log("trigger data stall recovery"); 386 mTimeLastRecoveryStartMs = SystemClock.elapsedRealtime(); 387 sendMessage(obtainMessage(EVENT_DO_RECOVERY)); 388 } 389 } 390 391 /** Reset the action to initial step. */ resetAction()392 private void resetAction() { 393 mTimeLastRecoveryStartMs = 0; 394 mMobileDataChangedToEnabledDuringDataStall = false; 395 mRadioStateChangedDuringDataStall = false; 396 mIsAirPlaneModeEnableDuringDataStall = false; 397 setRecoveryAction(RECOVERY_ACTION_GET_DATA_CALL_LIST); 398 } 399 400 /** 401 * Get recovery action from settings. 402 * 403 * @return recovery action 404 */ 405 @VisibleForTesting 406 @RecoveryAction getRecoveryAction()407 public int getRecoveryAction() { 408 log("getRecoveryAction: " + recoveryActionToString(mRecovryAction)); 409 return mRecovryAction; 410 } 411 412 /** 413 * Put recovery action into settings. 414 * 415 * @param action The next recovery action. 416 */ 417 @VisibleForTesting setRecoveryAction(@ecoveryAction int action)418 public void setRecoveryAction(@RecoveryAction int action) { 419 mRecovryAction = action; 420 421 // Check if the mobile data enabled is TRUE, it means that the mobile data setting changed 422 // from DISABLED to ENABLED, we will set the next recovery action to 423 // RECOVERY_ACTION_RADIO_RESTART due to already did the RECOVERY_ACTION_CLEANUP. 424 if (mMobileDataChangedToEnabledDuringDataStall 425 && mRecovryAction < RECOVERY_ACTION_RADIO_RESTART) { 426 mRecovryAction = RECOVERY_ACTION_RADIO_RESTART; 427 } 428 // Check if the radio state changed from off to on, it means that the modem already 429 // did the radio restart, we will set the next action to RECOVERY_ACTION_RESET_MODEM. 430 if (mRadioStateChangedDuringDataStall 431 && mRadioPowerState == TelephonyManager.RADIO_POWER_ON) { 432 mRecovryAction = RECOVERY_ACTION_RESET_MODEM; 433 } 434 // To check the flag from DataConfigManager if we need to skip the step. 435 if (shouldSkipRecoveryAction(mRecovryAction)) { 436 switch (mRecovryAction) { 437 case RECOVERY_ACTION_GET_DATA_CALL_LIST: 438 setRecoveryAction(RECOVERY_ACTION_CLEANUP); 439 break; 440 case RECOVERY_ACTION_CLEANUP: 441 setRecoveryAction(RECOVERY_ACTION_RADIO_RESTART); 442 break; 443 case RECOVERY_ACTION_RADIO_RESTART: 444 setRecoveryAction(RECOVERY_ACTION_RESET_MODEM); 445 break; 446 case RECOVERY_ACTION_RESET_MODEM: 447 resetAction(); 448 break; 449 } 450 } 451 452 log("setRecoveryAction: " + recoveryActionToString(mRecovryAction)); 453 } 454 455 /** 456 * Check if recovery already started. 457 * 458 * @return {@code true} if recovery already started, {@code false} recovery not started. 459 */ isRecoveryAlreadyStarted()460 private boolean isRecoveryAlreadyStarted() { 461 return getRecoveryAction() != RECOVERY_ACTION_GET_DATA_CALL_LIST || mRecoveryTriggered; 462 } 463 464 /** 465 * Get elapsed time since last recovery. 466 * 467 * @return the time since last recovery started. 468 */ getElapsedTimeSinceRecoveryMs()469 private long getElapsedTimeSinceRecoveryMs() { 470 return (SystemClock.elapsedRealtime() - mTimeLastRecoveryStartMs); 471 } 472 473 /** 474 * Get duration time for current recovery action. 475 * 476 * @return the time duration for current recovery action. 477 */ getDurationOfCurrentRecoveryMs()478 private long getDurationOfCurrentRecoveryMs() { 479 return (SystemClock.elapsedRealtime() - mTimeElapsedOfCurrentAction); 480 } 481 482 /** 483 * Broadcast intent when data stall occurred. 484 * 485 * @param recoveryAction Send the data stall detected intent with RecoveryAction info. 486 */ broadcastDataStallDetected(@ecoveryAction int recoveryAction)487 private void broadcastDataStallDetected(@RecoveryAction int recoveryAction) { 488 log("broadcastDataStallDetected recoveryAction: " + recoveryAction); 489 Intent intent = new Intent(TelephonyManager.ACTION_DATA_STALL_DETECTED); 490 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId()); 491 intent.putExtra(TelephonyManager.EXTRA_RECOVERY_ACTION, recoveryAction); 492 mPhone.getContext().sendBroadcast(intent); 493 } 494 495 /** Recovery Action: RECOVERY_ACTION_GET_DATA_CALL_LIST */ getDataCallList()496 private void getDataCallList() { 497 log("getDataCallList: request data call list"); 498 mWwanDataServiceManager.requestDataCallList(null); 499 } 500 501 /** Recovery Action: RECOVERY_ACTION_CLEANUP */ cleanUpDataNetwork()502 private void cleanUpDataNetwork() { 503 log("cleanUpDataNetwork: notify clean up data network"); 504 mDataStallRecoveryManagerCallback.invokeFromExecutor( 505 () -> mDataStallRecoveryManagerCallback.onDataStallReestablishInternet()); 506 } 507 508 /** Recovery Action: RECOVERY_ACTION_RADIO_RESTART */ powerOffRadio()509 private void powerOffRadio() { 510 log("powerOffRadio: Restart radio"); 511 mPhone.getServiceStateTracker().powerOffRadioSafely(); 512 } 513 514 /** Recovery Action: RECOVERY_ACTION_RESET_MODEM */ rebootModem()515 private void rebootModem() { 516 log("rebootModem: reboot modem"); 517 mPhone.rebootModem(null); 518 } 519 520 /** 521 * Initialize the network check timer. 522 * 523 * @param action The recovery action to start the network check timer. 524 */ startNetworkCheckTimer(@ecoveryAction int action)525 private void startNetworkCheckTimer(@RecoveryAction int action) { 526 // Ignore send message delayed due to reached the last action. 527 if (action == RECOVERY_ACTION_RESET_MODEM) return; 528 log("startNetworkCheckTimer(): " + getDataStallRecoveryDelayMillis(action) + "ms"); 529 if (!mNetworkCheckTimerStarted) { 530 mNetworkCheckTimerStarted = true; 531 mTimeLastRecoveryStartMs = SystemClock.elapsedRealtime(); 532 sendMessageDelayed( 533 obtainMessage(EVENT_DO_RECOVERY), getDataStallRecoveryDelayMillis(action)); 534 } 535 } 536 537 /** Cancel the network check timer. */ cancelNetworkCheckTimer()538 private void cancelNetworkCheckTimer() { 539 log("cancelNetworkCheckTimer()"); 540 if (mNetworkCheckTimerStarted) { 541 mNetworkCheckTimerStarted = false; 542 removeMessages(EVENT_DO_RECOVERY); 543 } 544 } 545 546 /** 547 * Check the conditions if we need to do recovery action. 548 * 549 * @param isNeedToCheckTimer {@code true} indicating we need the check timer when 550 * we receive the internet validation status changed. 551 * @return {@code true} if need to do recovery action, {@code false} no need to do recovery 552 * action. 553 */ isRecoveryNeeded(boolean isNeedToCheckTimer)554 private boolean isRecoveryNeeded(boolean isNeedToCheckTimer) { 555 logv("enter: isRecoveryNeeded()"); 556 557 // Skip if network is invalid and recovery was not started yet 558 if (!mIsValidNetwork && !isRecoveryAlreadyStarted()) { 559 logl("skip when network still remains invalid and recovery was not started yet"); 560 return false; 561 } 562 563 // Skip recovery if we have already attempted all steps. 564 if (mIsAttemptedAllSteps) { 565 logl("skip retrying continue recovery action"); 566 return false; 567 } 568 569 // To avoid back to back recovery, wait for a grace period 570 if (getElapsedTimeSinceRecoveryMs() < getDataStallRecoveryDelayMillis(mLastAction) 571 && isNeedToCheckTimer) { 572 logl("skip back to back data stall recovery"); 573 return false; 574 } 575 576 // Skip recovery if it can cause a call to drop 577 if (mPhone.getState() != PhoneConstants.State.IDLE 578 && getRecoveryAction() > RECOVERY_ACTION_CLEANUP) { 579 logl("skip data stall recovery as there is an active call"); 580 return false; 581 } 582 583 // Skip when poor signal strength 584 if (mPhone.getSignalStrength().getLevel() <= CellSignalStrength.SIGNAL_STRENGTH_POOR) { 585 logl("skip data stall recovery as in poor signal condition"); 586 return false; 587 } 588 589 if (!mDataNetworkController.isInternetDataAllowed()) { 590 logl("skip data stall recovery as data not allowed."); 591 return false; 592 } 593 594 if (!mIsInternetNetworkConnected) { 595 logl("skip data stall recovery as data not connected"); 596 return false; 597 } 598 return true; 599 } 600 601 /** 602 * Set the validation status into metrics. 603 * 604 * @param isValid true for validation passed & false for validation failed 605 */ setNetworkValidationState(boolean isValid)606 private void setNetworkValidationState(boolean isValid) { 607 boolean isLogNeeded = false; 608 int timeDuration = 0; 609 int timeDurationOfCurrentAction = 0; 610 boolean isFirstDataStall = false; 611 boolean isFirstValidationAfterDoRecovery = false; 612 @RecoveredReason int reason = getRecoveredReason(isValid); 613 // Validation status is true and was not data stall. 614 if (isValid && !mDataStalled) { 615 return; 616 } 617 618 if (!mDataStalled) { 619 // First data stall 620 isLogNeeded = true; 621 mDataStalled = true; 622 isFirstDataStall = true; 623 mDataStallStartMs = SystemClock.elapsedRealtime(); 624 logl("data stall: start time = " + DataUtils.elapsedTimeToString(mDataStallStartMs)); 625 } else if (!mLastActionReported) { 626 // When the first validation status appears, enter this block. 627 isLogNeeded = true; 628 timeDuration = (int) (SystemClock.elapsedRealtime() - mDataStallStartMs); 629 mLastActionReported = true; 630 isFirstValidationAfterDoRecovery = true; 631 } 632 633 if (isValid) { 634 // When the validation passed(mobile data resume), enter this block. 635 isLogNeeded = true; 636 timeDuration = (int) (SystemClock.elapsedRealtime() - mDataStallStartMs); 637 mLastActionReported = false; 638 mDataStalled = false; 639 } 640 641 if (isLogNeeded) { 642 timeDurationOfCurrentAction = 643 (isFirstDataStall == true ? 0 : (int) getDurationOfCurrentRecoveryMs()); 644 DataStallRecoveryStats.onDataStallEvent( 645 mLastAction, mPhone, isValid, timeDuration, reason, 646 isFirstValidationAfterDoRecovery, timeDurationOfCurrentAction); 647 logl( 648 "data stall: " 649 + (isFirstDataStall == true ? "start" : isValid == false ? "in process" : "end") 650 + ", lastaction=" 651 + recoveryActionToString(mLastAction) 652 + ", isRecovered=" 653 + isValid 654 + ", reason=" 655 + recoveredReasonToString(reason) 656 + ", isFirstValidationAfterDoRecovery=" 657 + isFirstValidationAfterDoRecovery 658 + ", TimeDuration=" 659 + timeDuration 660 + ", TimeDurationForCurrentRecoveryAction=" 661 + timeDurationOfCurrentAction); 662 } 663 } 664 665 /** 666 * Get the data stall recovered reason. 667 * 668 * @param isValid true for validation passed & false for validation failed 669 */ 670 @RecoveredReason getRecoveredReason(boolean isValid)671 private int getRecoveredReason(boolean isValid) { 672 if (!isValid) return RECOVERED_REASON_NONE; 673 674 int ret = RECOVERED_REASON_DSRM; 675 if (mRadioStateChangedDuringDataStall) { 676 if (mLastAction <= RECOVERY_ACTION_CLEANUP) { 677 ret = RECOVERED_REASON_MODEM; 678 } 679 if (mLastAction > RECOVERY_ACTION_CLEANUP) { 680 ret = RECOVERED_REASON_DSRM; 681 } 682 if (mIsAirPlaneModeEnableDuringDataStall) { 683 ret = RECOVERED_REASON_USER; 684 } 685 } else if (mMobileDataChangedToEnabledDuringDataStall) { 686 ret = RECOVERED_REASON_USER; 687 } 688 return ret; 689 } 690 691 /** Perform a series of data stall recovery actions. */ doRecovery()692 private void doRecovery() { 693 @RecoveryAction final int recoveryAction = getRecoveryAction(); 694 final int signalStrength = mPhone.getSignalStrength().getLevel(); 695 mRecoveryTriggered = true; 696 697 // DSRM used sendMessageDelayed to process the next event EVENT_DO_RECOVERY, so it need 698 // to check the condition if DSRM need to process the recovery action. 699 if (!isRecoveryNeeded(false)) { 700 cancelNetworkCheckTimer(); 701 startNetworkCheckTimer(recoveryAction); 702 return; 703 } 704 705 TelephonyMetrics.getInstance() 706 .writeSignalStrengthEvent(mPhone.getPhoneId(), signalStrength); 707 TelephonyMetrics.getInstance().writeDataStallEvent(mPhone.getPhoneId(), recoveryAction); 708 mLastAction = recoveryAction; 709 mLastActionReported = false; 710 broadcastDataStallDetected(recoveryAction); 711 mNetworkCheckTimerStarted = false; 712 mTimeElapsedOfCurrentAction = SystemClock.elapsedRealtime(); 713 714 switch (recoveryAction) { 715 case RECOVERY_ACTION_GET_DATA_CALL_LIST: 716 logl("doRecovery(): get data call list"); 717 getDataCallList(); 718 setRecoveryAction(RECOVERY_ACTION_CLEANUP); 719 break; 720 case RECOVERY_ACTION_CLEANUP: 721 logl("doRecovery(): cleanup all connections"); 722 cleanUpDataNetwork(); 723 setRecoveryAction(RECOVERY_ACTION_RADIO_RESTART); 724 break; 725 case RECOVERY_ACTION_RADIO_RESTART: 726 logl("doRecovery(): restarting radio"); 727 setRecoveryAction(RECOVERY_ACTION_RESET_MODEM); 728 powerOffRadio(); 729 break; 730 case RECOVERY_ACTION_RESET_MODEM: 731 logl("doRecovery(): modem reset"); 732 rebootModem(); 733 resetAction(); 734 mIsAttemptedAllSteps = true; 735 break; 736 default: 737 throw new RuntimeException( 738 "doRecovery: Invalid recoveryAction = " 739 + recoveryActionToString(recoveryAction)); 740 } 741 742 startNetworkCheckTimer(mLastAction); 743 } 744 745 /** 746 * Convert @RecoveredReason to string 747 * 748 * @param reason The recovered reason. 749 * @return The recovered reason in string format. 750 */ recoveredReasonToString(@ecoveredReason int reason)751 private static @NonNull String recoveredReasonToString(@RecoveredReason int reason) { 752 switch (reason) { 753 case RECOVERED_REASON_NONE: 754 return "RECOVERED_REASON_NONE"; 755 case RECOVERED_REASON_DSRM: 756 return "RECOVERED_REASON_DSRM"; 757 case RECOVERED_REASON_MODEM: 758 return "RECOVERED_REASON_MODEM"; 759 case RECOVERED_REASON_USER: 760 return "RECOVERED_REASON_USER"; 761 default: 762 return "Unknown(" + reason + ")"; 763 } 764 } 765 766 /** 767 * Convert RadioPowerState to string 768 * 769 * @param state The radio power state 770 * @return The radio power state in string format. 771 */ radioPowerStateToString(@adioPowerState int state)772 private static @NonNull String radioPowerStateToString(@RadioPowerState int state) { 773 switch (state) { 774 case TelephonyManager.RADIO_POWER_OFF: 775 return "RADIO_POWER_OFF"; 776 case TelephonyManager.RADIO_POWER_ON: 777 return "RADIO_POWER_ON"; 778 case TelephonyManager.RADIO_POWER_UNAVAILABLE: 779 return "RADIO_POWER_UNAVAILABLE"; 780 default: 781 return "Unknown(" + state + ")"; 782 } 783 } 784 785 /** 786 * Convert RecoveryAction to string 787 * 788 * @param action The recovery action 789 * @return The recovery action in string format. 790 */ recoveryActionToString(@ecoveryAction int action)791 private static @NonNull String recoveryActionToString(@RecoveryAction int action) { 792 switch (action) { 793 case RECOVERY_ACTION_GET_DATA_CALL_LIST: 794 return "RECOVERY_ACTION_GET_DATA_CALL_LIST"; 795 case RECOVERY_ACTION_CLEANUP: 796 return "RECOVERY_ACTION_CLEANUP"; 797 case RECOVERY_ACTION_RADIO_RESTART: 798 return "RECOVERY_ACTION_RADIO_RESTART"; 799 case RECOVERY_ACTION_RESET_MODEM: 800 return "RECOVERY_ACTION_RESET_MODEM"; 801 default: 802 return "Unknown(" + action + ")"; 803 } 804 } 805 806 /** 807 * Log debug messages. 808 * 809 * @param s debug messages 810 */ log(@onNull String s)811 private void log(@NonNull String s) { 812 Rlog.d(mLogTag, s); 813 } 814 815 /** 816 * Log verbose messages. 817 * 818 * @param s debug messages. 819 */ logv(@onNull String s)820 private void logv(@NonNull String s) { 821 if (VDBG) Rlog.v(mLogTag, s); 822 } 823 824 /** 825 * Log error messages. 826 * 827 * @param s error messages 828 */ loge(@onNull String s)829 private void loge(@NonNull String s) { 830 Rlog.e(mLogTag, s); 831 } 832 833 /** 834 * Log debug messages and also log into the local log. 835 * 836 * @param s debug messages 837 */ logl(@onNull String s)838 private void logl(@NonNull String s) { 839 log(s); 840 mLocalLog.log(s); 841 } 842 843 /** 844 * Dump the state of DataStallRecoveryManager 845 * 846 * @param fd File descriptor 847 * @param printWriter Print writer 848 * @param args Arguments 849 */ dump(FileDescriptor fd, PrintWriter printWriter, String[] args)850 public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) { 851 IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); 852 pw.println( 853 DataStallRecoveryManager.class.getSimpleName() + "-" + mPhone.getPhoneId() + ":"); 854 pw.increaseIndent(); 855 856 pw.println("mIsValidNetwork=" + mIsValidNetwork); 857 pw.println("mIsInternetNetworkConnected=" + mIsInternetNetworkConnected); 858 pw.println("mIsAirPlaneModeEnableDuringDataStall=" + mIsAirPlaneModeEnableDuringDataStall); 859 pw.println("mDataStalled=" + mDataStalled); 860 pw.println("mLastAction=" + recoveryActionToString(mLastAction)); 861 pw.println("mIsAttemptedAllSteps=" + mIsAttemptedAllSteps); 862 pw.println("mDataStallStartMs=" + DataUtils.elapsedTimeToString(mDataStallStartMs)); 863 pw.println("mRadioPowerState=" + radioPowerStateToString(mRadioPowerState)); 864 pw.println("mLastActionReported=" + mLastActionReported); 865 pw.println("mTimeLastRecoveryStartMs=" 866 + DataUtils.elapsedTimeToString(mTimeLastRecoveryStartMs)); 867 pw.println("getRecoveryAction()=" + recoveryActionToString(getRecoveryAction())); 868 pw.println("mRadioStateChangedDuringDataStall=" + mRadioStateChangedDuringDataStall); 869 pw.println( 870 "mMobileDataChangedToEnabledDuringDataStall=" 871 + mMobileDataChangedToEnabledDuringDataStall); 872 pw.println( 873 "DataStallRecoveryDelayMillisArray=" 874 + Arrays.toString(mDataStallRecoveryDelayMillisArray)); 875 pw.println("SkipRecoveryActionArray=" + Arrays.toString(mSkipRecoveryActionArray)); 876 pw.decreaseIndent(); 877 pw.println(""); 878 879 pw.println("Local logs:"); 880 pw.increaseIndent(); 881 mLocalLog.dump(fd, pw, args); 882 pw.decreaseIndent(); 883 pw.decreaseIndent(); 884 } 885 } 886