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