• 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 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