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