• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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