Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0f4a5449 authored by Jayachandran Chinnakkannu's avatar Jayachandran Chinnakkannu Committed by Gerrit Code Review
Browse files

Merge "Cellular data stall recovery based on networkStatus"

parents 41b14d40 8e3573e1
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ java_library {
        "android.hardware.radio.config-V1.2-java",
        "android.hardware.radio.deprecated-V1.0-java",
        "android.hidl.base-V1.0-java",
	"android-support-annotations",
    ],

    product_variables: {
+4 −9
Original line number Diff line number Diff line
@@ -2200,16 +2200,11 @@ public class DataConnection extends StateMachine {

        @Override
        protected void networkStatus(int status, String redirectUrl) {
            if(!TextUtils.isEmpty(redirectUrl)) {
            log("validation status: " + status + " with redirection URL: " + redirectUrl);
                /* its possible that we have multiple DataConnection with INTERNET_CAPABILITY
                   all fail the validation with the same redirection url, send CMD back to DCTracker
                   and let DcTracker to make the decision */
                Message msg = mDct.obtainMessage(DctConstants.EVENT_REDIRECTION_DETECTED,
                        redirectUrl);
            Message msg = mDct.obtainMessage(DctConstants.EVENT_NETWORK_STATUS_CHANGED,
                    status, 0, redirectUrl);
            msg.sendToTarget();
        }
        }

        @Override
        public void sendNetworkCapabilities(NetworkCapabilities networkCapabilities) {
+203 −82
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@

package com.android.internal.telephony.dataconnection;

import static android.Manifest.permission.READ_PHONE_STATE;
import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;

import static com.android.internal.telephony.RILConstants.DATA_PROFILE_DEFAULT;
import static com.android.internal.telephony.RILConstants.DATA_PROFILE_INVALID;
@@ -38,6 +38,7 @@ import android.database.ContentObserver;
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.LinkProperties;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
import android.net.NetworkConfig;
import android.net.NetworkRequest;
@@ -59,6 +60,7 @@ import android.preference.PreferenceManager;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.provider.Telephony;
import android.support.annotation.IntDef;
import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.CarrierConfigManager;
import android.telephony.CellLocation;
@@ -102,6 +104,8 @@ import com.android.internal.util.AsyncChannel;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
@@ -487,7 +491,7 @@ public class DcTracker extends Handler {
    // Reference counter for enabling fail fast
    private static int sEnableFailFastRefCounter = 0;
    // True if data stall detection is enabled
    private volatile boolean mDataStallDetectionEnabled = true;
    private volatile boolean mDataStallNoRxEnabled = true;

    private volatile boolean mFailFast = false;

@@ -573,6 +577,8 @@ public class DcTracker extends Handler {

    private final int mTransportType;

    private DataStallRecoveryHandler mDsRecoveryHandler;

    //***** Constructor
    public DcTracker(Phone phone, int transportType) {
        super();
@@ -597,6 +603,8 @@ public class DcTracker extends Handler {
        mAlarmManager =
                (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);

        mDsRecoveryHandler = new DataStallRecoveryHandler();

        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_ON);
        filter.addAction(Intent.ACTION_SCREEN_OFF);
@@ -2024,7 +2032,11 @@ public class DcTracker extends Handler {
         */

        int reset = Integer.parseInt(SystemProperties.get("net.ppp.reset-by-timeout", "0"));
        try {
            SystemProperties.set("net.ppp.reset-by-timeout", String.valueOf(reset + 1));
        } catch (RuntimeException ex) {
            log("Failed to set net.ppp.reset-by-timeout");
        }
    }

    /**
@@ -2792,14 +2804,30 @@ public class DcTracker extends Handler {
    }

    /**
     * Called when EVENT_REDIRECTION_DETECTED is received.
     * Called when EVENT_NETWORK_STATUS_CHANGED is received.
     *
     * @param status One of {@code NetworkAgent.VALID_NETWORK} or
     * {@code NetworkAgent.INVALID_NETWORK}.
     * @param redirectUrl If the Internet probe was redirected, this
     * is the destination it was redirected to, otherwise {@code null}
     */
    private void onDataConnectionRedirected(String redirectUrl) {
    private void onNetworkStatusChanged(int status, String redirectUrl) {
        if (!TextUtils.isEmpty(redirectUrl)) {
            Intent intent = new Intent(TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED);
            intent.putExtra(TelephonyIntents.EXTRA_REDIRECTION_URL_KEY, redirectUrl);
            mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(intent);
            log("Notify carrier signal receivers with redirectUrl: " + redirectUrl);
        } else {
            final boolean isValid = status == NetworkAgent.VALID_NETWORK;
            if (!mDsRecoveryHandler.isRecoveryOnBadNetworkEnabled()) {
                if (DBG) log("Skip data stall recovery on network status change with in threshold");
                return;
            }
            if (mTransportType != TransportType.WWAN) {
                if (DBG) log("Skip data stall recovery on non WWAN");
                return;
            }
            mDsRecoveryHandler.processNetworkStatusChanged(isValid);
        }
    }

@@ -3323,7 +3351,7 @@ public class DcTracker extends Handler {
                break;

            case DctConstants.EVENT_DO_RECOVERY:
                doRecovery();
                mDsRecoveryHandler.doRecovery();
                break;

            case DctConstants.EVENT_APN_CHANGED:
@@ -3432,6 +3460,7 @@ public class DcTracker extends Handler {
            case DctConstants.EVENT_ROAMING_SETTING_CHANGE:
                onDataRoamingOnOrSettingsChanged(msg.what);
                break;

            case DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE:
                // Update sharedPreference to false when exits new device provisioning, indicating
                // no users modifications on the settings for new devices. Thus carrier specific
@@ -3442,10 +3471,11 @@ public class DcTracker extends Handler {
                    sp.edit().putBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, false).commit();
                }
                break;
            case DctConstants.EVENT_REDIRECTION_DETECTED:

            case DctConstants.EVENT_NETWORK_STATUS_CHANGED:
                int status = msg.arg1;
                String url = (String) msg.obj;
                log("dataConnectionTracker.handleMessage: EVENT_REDIRECTION_DETECTED=" + url);
                onDataConnectionRedirected(url);
                onNetworkStatusChanged(status, url);
                break;

            case DctConstants.EVENT_RADIO_AVAILABLE:
@@ -3529,8 +3559,8 @@ public class DcTracker extends Handler {
                if (mFailFast != enabled) {
                    mFailFast = enabled;

                    mDataStallDetectionEnabled = !enabled;
                    if (mDataStallDetectionEnabled
                    mDataStallNoRxEnabled = !enabled;
                    if (mDsRecoveryHandler.isNoRxDataStallDetectionEnabled()
                            && (getOverallState() == DctConstants.State.CONNECTED)
                            && (!mInVoiceCall ||
                                    mPhone.getServiceStateTracker()
@@ -3807,7 +3837,7 @@ public class DcTracker extends Handler {
        pw.println(" mNetStatPollEnabled=" + mNetStatPollEnabled);
        pw.println(" mDataStallTxRxSum=" + mDataStallTxRxSum);
        pw.println(" mDataStallAlarmTag=" + mDataStallAlarmTag);
        pw.println(" mDataStallDetectionEnabled=" + mDataStallDetectionEnabled);
        pw.println(" mDataStallNoRxEnabled=" + mDataStallNoRxEnabled);
        pw.println(" mSentSinceLastRecv=" + mSentSinceLastRecv);
        pw.println(" mNoRecvPollCount=" + mNoRecvPollCount);
        pw.println(" mResolver=" + mResolver);
@@ -4198,80 +4228,172 @@ public class DcTracker extends Handler {
    /**
     * Data-Stall
     */

    // Recovery action taken in case of data stall
    private static class RecoveryAction {
        public static final int GET_DATA_CALL_LIST      = 0;
        public static final int CLEANUP                 = 1;
        public static final int REREGISTER              = 2;
        public static final int RADIO_RESTART           = 3;
    @IntDef(
        value = {
            RECOVERY_ACTION_GET_DATA_CALL_LIST,
            RECOVERY_ACTION_CLEANUP,
            RECOVERY_ACTION_REREGISTER,
            RECOVERY_ACTION_RADIO_RESTART
        })
    @Retention(RetentionPolicy.SOURCE)
    private @interface RecoveryAction {};
    private static final int RECOVERY_ACTION_GET_DATA_CALL_LIST      = 0;
    private static final int RECOVERY_ACTION_CLEANUP                 = 1;
    private static final int RECOVERY_ACTION_REREGISTER              = 2;
    private static final int RECOVERY_ACTION_RADIO_RESTART           = 3;

    // Recovery handler class for cellular data stall
    private class DataStallRecoveryHandler {
        // Default minimum duration between each recovery steps
        private static final int
                DEFAULT_MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS = (3 * 60 * 1000); // 3 mins

        // The elapsed real time of last recovery attempted
        private long mTimeLastRecoveryStartMs;
        // Whether current network good or not
        private boolean mIsValidNetwork;

        public DataStallRecoveryHandler() {
            reset();
        }

        public void reset() {
            mTimeLastRecoveryStartMs = 0;
            putRecoveryAction(RECOVERY_ACTION_GET_DATA_CALL_LIST);
        }

        private static boolean isAggressiveRecovery(int value) {
            return ((value == RecoveryAction.CLEANUP) ||
                    (value == RecoveryAction.REREGISTER) ||
                    (value == RecoveryAction.RADIO_RESTART));
        public boolean isAggressiveRecovery() {
            @RecoveryAction int action = getRecoveryAction();

            return ((action == RECOVERY_ACTION_CLEANUP)
                    || (action == RECOVERY_ACTION_REREGISTER)
                    || (action == RECOVERY_ACTION_RADIO_RESTART));
        }

        private long getMinDurationBetweenRecovery() {
            return Settings.Global.getLong(mResolver,
                Settings.Global.MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS,
                DEFAULT_MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS);
        }

        private long getElapsedTimeSinceRecoveryMs() {
            return (SystemClock.elapsedRealtime() - mTimeLastRecoveryStartMs);
        }

        @RecoveryAction
        private int getRecoveryAction() {
        int action = Settings.System.getInt(mResolver,
                "radio.data.stall.recovery.action", RecoveryAction.GET_DATA_CALL_LIST);
            @RecoveryAction int action = Settings.System.getInt(mResolver,
                    "radio.data.stall.recovery.action", RECOVERY_ACTION_GET_DATA_CALL_LIST);
            if (VDBG_STALL) log("getRecoveryAction: " + action);
            return action;
        }

    private void putRecoveryAction(int action) {
        private void putRecoveryAction(@RecoveryAction int action) {
            Settings.System.putInt(mResolver, "radio.data.stall.recovery.action", action);
            if (VDBG_STALL) log("putRecoveryAction: " + action);
        }

    private void broadcastDataStallDetected(int recoveryAction) {
        private void broadcastDataStallDetected(@RecoveryAction int recoveryAction) {
            Intent intent = new Intent(TelephonyManager.ACTION_DATA_STALL_DETECTED);
            SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
            intent.putExtra(TelephonyManager.EXTRA_RECOVERY_ACTION, recoveryAction);
        mPhone.getContext().sendBroadcast(intent, READ_PHONE_STATE);
            mPhone.getContext().sendBroadcast(intent, READ_PRIVILEGED_PHONE_STATE);
        }

        private boolean isRecoveryAlreadyStarted() {
            return getRecoveryAction() != RECOVERY_ACTION_GET_DATA_CALL_LIST;
        }

    private void doRecovery() {
        private boolean checkRecovery() {
            // To avoid back to back recovery wait for a grace period
            if (getElapsedTimeSinceRecoveryMs() < getMinDurationBetweenRecovery()) {
                if (VDBG_STALL) log("skip back to back data stall recovery");
                return false;
            }
            // Data is not allowed in current environment
            if (!isDataAllowed(null, null)) {
                log("skipped data stall recovery due to data is not allowd");
                return false;
            }
            return true;
        }

        private void triggerRecovery() {
            sendMessage(obtainMessage(DctConstants.EVENT_DO_RECOVERY));
        }

        public void doRecovery() {
            if (getOverallState() == DctConstants.State.CONNECTED) {
                // Go through a series of recovery steps, each action transitions to the next action
            final int recoveryAction = getRecoveryAction();
            TelephonyMetrics.getInstance().writeDataStallEvent(mPhone.getPhoneId(), recoveryAction);
                @RecoveryAction final int recoveryAction = getRecoveryAction();
                TelephonyMetrics.getInstance().writeDataStallEvent(
                        mPhone.getPhoneId(), recoveryAction);
                broadcastDataStallDetected(recoveryAction);

                switch (recoveryAction) {
                case RecoveryAction.GET_DATA_CALL_LIST:
                    case RECOVERY_ACTION_GET_DATA_CALL_LIST:
                        EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_GET_DATA_CALL_LIST,
                            mSentSinceLastRecv);
                        if (DBG) log("doRecovery() get data call list");
                        mDataServiceManager.getDataCallList(obtainMessage());
                    putRecoveryAction(RecoveryAction.CLEANUP);
                        putRecoveryAction(RECOVERY_ACTION_CLEANUP);
                        break;
                case RecoveryAction.CLEANUP:
                    case RECOVERY_ACTION_CLEANUP:
                        EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_CLEANUP,
                            mSentSinceLastRecv);
                        if (DBG) log("doRecovery() cleanup all connections");
                    cleanUpAllConnectionsInternal(true, Phone.REASON_PDP_RESET);
                    putRecoveryAction(RecoveryAction.REREGISTER);
                        cleanUpAllConnections(Phone.REASON_PDP_RESET);
                        putRecoveryAction(RECOVERY_ACTION_REREGISTER);
                        break;
                case RecoveryAction.REREGISTER:
                    case RECOVERY_ACTION_REREGISTER:
                        EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_REREGISTER,
                            mSentSinceLastRecv);
                        if (DBG) log("doRecovery() re-register");
                        mPhone.getServiceStateTracker().reRegisterNetwork(null);
                    putRecoveryAction(RecoveryAction.RADIO_RESTART);
                        putRecoveryAction(RECOVERY_ACTION_RADIO_RESTART);
                        break;
                case RecoveryAction.RADIO_RESTART:
                    case RECOVERY_ACTION_RADIO_RESTART:
                        EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART,
                            mSentSinceLastRecv);
                        if (DBG) log("restarting radio");
                        restartRadio();
                    putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
                        reset();
                        break;
                    default:
                        throw new RuntimeException("doRecovery: Invalid recoveryAction="
                            + recoveryAction);
                }
                mSentSinceLastRecv = 0;
                mTimeLastRecoveryStartMs = SystemClock.elapsedRealtime();
            }
        }

        public void processNetworkStatusChanged(boolean isValid) {
            if (isValid) {
                mIsValidNetwork = true;
                reset();
            } else {
                if (mIsValidNetwork || isRecoveryAlreadyStarted()) {
                    mIsValidNetwork = false;
                    // Check and trigger a recovery if network switched from good
                    // to bad or recovery is already started before.
                    if (checkRecovery()) {
                        if (DBG) log("trigger data stall recovery");
                        triggerRecovery();
                    }
                }
            }
        }

        public boolean isRecoveryOnBadNetworkEnabled() {
            return Settings.Global.getInt(mResolver,
                    Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 1) == 1;
        }

        public boolean isNoRxDataStallDetectionEnabled() {
            return mDataStallNoRxEnabled && !isRecoveryOnBadNetworkEnabled();
        }
    }

@@ -4298,7 +4420,7 @@ public class DcTracker extends Handler {
        if ( sent > 0 && received > 0 ) {
            if (VDBG_STALL) log("updateDataStallInfo: IN/OUT");
            mSentSinceLastRecv = 0;
            putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
            mDsRecoveryHandler.reset();
        } else if (sent > 0 && received == 0) {
            if (isPhoneStateIdle()) {
                mSentSinceLastRecv += sent;
@@ -4312,7 +4434,7 @@ public class DcTracker extends Handler {
        } else if (sent == 0 && received > 0) {
            if (VDBG_STALL) log("updateDataStallInfo: IN");
            mSentSinceLastRecv = 0;
            putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
            mDsRecoveryHandler.reset();
        } else {
            if (VDBG_STALL) log("updateDataStallInfo: NONE");
        }
@@ -4347,7 +4469,8 @@ public class DcTracker extends Handler {
        boolean suspectedStall = DATA_STALL_NOT_SUSPECTED;
        if (mSentSinceLastRecv >= hangWatchdogTrigger) {
            if (DBG) {
                log("onDataStallAlarm: tag=" + tag + " do recovery action=" + getRecoveryAction());
                log("onDataStallAlarm: tag=" + tag + " do recovery action="
                        + mDsRecoveryHandler.getRecoveryAction());
            }
            suspectedStall = DATA_STALL_SUSPECTED;
            sendMessage(obtainMessage(DctConstants.EVENT_DO_RECOVERY));
@@ -4361,13 +4484,13 @@ public class DcTracker extends Handler {
    }

    private void startDataStallAlarm(boolean suspectedStall) {
        int nextAction = getRecoveryAction();
        int delayInMs;

        if (mDataStallDetectionEnabled && getOverallState() == DctConstants.State.CONNECTED) {
        if (mDsRecoveryHandler.isNoRxDataStallDetectionEnabled()
                && getOverallState() == DctConstants.State.CONNECTED) {
            // If screen is on or data stall is currently suspected, set the alarm
            // with an aggressive timeout.
            if (mIsScreenOn || suspectedStall || RecoveryAction.isAggressiveRecovery(nextAction)) {
            if (mIsScreenOn || suspectedStall || mDsRecoveryHandler.isAggressiveRecovery()) {
                delayInMs = Settings.Global.getInt(mResolver,
                        Settings.Global.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS,
                        DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS_DEFAULT);
@@ -4413,9 +4536,7 @@ public class DcTracker extends Handler {
        if (isConnected() == false) return;
        // To be called on screen status change.
        // Do not cancel the alarm if it is set with aggressive timeout.
        int nextAction = getRecoveryAction();

        if (RecoveryAction.isAggressiveRecovery(nextAction)) {
        if (mDsRecoveryHandler.isAggressiveRecovery()) {
            if (DBG) log("restartDataStallAlarm: action is pending. not resetting the alarm.");
            return;
        }
+6 −2
Original line number Diff line number Diff line
@@ -45,7 +45,6 @@ import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.telephony.data.DataCallResponse;
import android.telephony.data.DataProfile;
import android.telephony.emergency.EmergencyNumber;

@@ -540,7 +539,12 @@ public class SimulatedCommands extends BaseCommands
     */
    @Override
    public void getDataCallList(Message result) {
        resultSuccess(result, new ArrayList<DataCallResponse>(0));
        ArrayList<SetupDataCallResult> dcCallList = new ArrayList<SetupDataCallResult>(0);
        SimulatedCommandsVerifier.getInstance().getDataCallList(result);
        if (mSetupDataCallResult != null) {
            dcCallList.add(mSetupDataCallResult);
        }
        resultSuccess(result, dcCallList);
    }

    /**
+215 −0

File changed.

Preview size limit exceeded, changes collapsed.