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

Commit 45d990f2 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Collect link status & bandwidth for DataStallRecoveryStats" into udc-d1-dev

parents 372f530c 037253af
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -187,6 +187,7 @@ public class DataStallRecoveryManager extends Handler {

    private DataStallRecoveryManagerCallback mDataStallRecoveryManagerCallback;

    private final DataStallRecoveryStats mStats;
    /**
     * The data stall recovery manager callback. Note this is only used for passing information
     * internally in the data stack, should not be used externally.
@@ -248,6 +249,8 @@ public class DataStallRecoveryManager extends Handler {
        updateDataStallRecoveryConfigs();

        registerAllEvents();

        mStats = new DataStallRecoveryStats(mPhone, dataNetworkController);
    }

    /** Register for all events that data stall monitor is interested. */
@@ -645,8 +648,9 @@ public class DataStallRecoveryManager extends Handler {
                   && !mIsAttemptedAllSteps)
                 || mLastAction == RECOVERY_ACTION_RESET_MODEM)
                 ? (int) getDurationOfCurrentRecoveryMs() : 0;
            DataStallRecoveryStats.onDataStallEvent(
                    mLastAction, mPhone, isValid, timeDuration, reason,

            mStats.uploadMetrics(
                    mLastAction, isValid, timeDuration, reason,
                    isFirstValidationAfterDoRecovery, timeDurationOfCurrentAction);
            logl(
                    "data stall: "
+286 −88
Original line number Diff line number Diff line
@@ -16,138 +16,336 @@

package com.android.internal.telephony.metrics;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.NetworkCapabilities;
import android.os.Handler;
import android.os.HandlerThread;
import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation.NetworkType;
import android.telephony.CellSignalStrength;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.telephony.data.DataCallResponse;
import android.telephony.data.DataCallResponse.LinkStatus;

import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.TelephonyStatsLog;
import com.android.internal.telephony.data.DataNetwork;
import com.android.internal.telephony.data.DataNetworkController;
import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
import com.android.internal.telephony.data.DataStallRecoveryManager;
import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.telephony.Rlog;

/** Generates metrics related to data stall recovery events per phone ID for the pushed atom. */
import java.util.List;

/**
 * Generates metrics related to data stall recovery events per phone ID for the pushed atom.
 */
public class DataStallRecoveryStats {

    /**
     * Create and push new atom when there is a data stall recovery event
     *
     * @param recoveryAction Data stall recovery action
     * @param phone
     * Value indicating that link bandwidth is unspecified.
     * Copied from {@code NetworkCapabilities#LINK_BANDWIDTH_UNSPECIFIED}
     */
    private static final int LINK_BANDWIDTH_UNSPECIFIED = 0;

    private static final String TAG = "DSRS-";

    // Handler to upload metrics.
    private final @NonNull Handler mHandler;

    private final @NonNull String mTag;
    private final @NonNull Phone mPhone;

    // The interface name of the internet network.
    private @Nullable String mIfaceName = null;

    /* Metrics and stats data variables */
    private int mPhoneId = 0;
    private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
    private int mSignalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
    private int mBand = 0;
    // The RAT used for data (including IWLAN).
    private @NetworkType int mRat = TelephonyManager.NETWORK_TYPE_UNKNOWN;
    private boolean mIsOpportunistic = false;
    private boolean mIsMultiSim = false;
    private int mNetworkRegState = NetworkRegistrationInfo
                    .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
    // Info of the other device in case of DSDS
    private int mOtherSignalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
    private int mOtherNetworkRegState = NetworkRegistrationInfo
                    .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
    // Link status of the data network
    private @LinkStatus int mInternetLinkStatus = DataCallResponse.LINK_STATUS_UNKNOWN;

    /* Since the Enum has been extended in Android T, we are mapping it to the correct number. */
    private static final int RECOVERY_ACTION_RADIO_RESTART_MAPPING = 3;
    private static final int RECOVERY_ACTION_RESET_MODEM_MAPPING = 4;
    // The link bandwidth of the data network
    private int mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
    private int mLinkUpBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;

    /**
     * Called when data stall happened.
     * Constructs a new instance of {@link DataStallRecoveryStats}.
     */
    public DataStallRecoveryStats(@NonNull final Phone phone,
            @NonNull final DataNetworkController dataNetworkController) {
        mTag = TAG + phone.getPhoneId();
        mPhone = phone;

        HandlerThread handlerThread = new HandlerThread(mTag + "-thread");
        handlerThread.start();
        mHandler = new Handler(handlerThread.getLooper());

        dataNetworkController.registerDataNetworkControllerCallback(
                new DataNetworkControllerCallback(mHandler::post) {
                @Override
                public void onInternetDataNetworkConnected(
                        @NonNull List<DataNetwork> internetNetworks) {
                    for (DataNetwork dataNetwork : internetNetworks) {
                        mIfaceName = dataNetwork.getLinkProperties().getInterfaceName();
                        break;
                    }
                }

                @Override
                public void onInternetDataNetworkDisconnected() {
                    mIfaceName = null;
                }

                @Override
                public void onPhysicalLinkStatusChanged(@LinkStatus int status) {
                    mInternetLinkStatus = status;
                }
            });
    }

    /**
     * Create and push new atom when there is a data stall recovery event.
     *
     * @param recoveryAction The recovery action.
     * @param phone The phone instance.
     * @param isRecovered The data stall symptom recovered or not.
     * @param durationMillis The duration from data stall symptom occurred.
     * @param reason The recovered(data resume) reason.
     * @param isFirstValidation The validation status if it's the first come after recovery.
     * @param action The recovery action.
     * @param isRecovered Whether the data stall has been recovered.
     * @param duration The duration from data stall occurred in milliseconds.
     * @param reason The reason for the recovery.
     * @param isFirstValidation Whether this is the first validation after recovery.
     * @param durationOfAction The duration of the current action in milliseconds.
     */
    public static void onDataStallEvent(
            @DataStallRecoveryManager.RecoveryAction int recoveryAction,
            Phone phone,
    public void uploadMetrics(
            @DataStallRecoveryManager.RecoveryAction int action,
            boolean isRecovered,
            int durationMillis,
            int duration,
            @DataStallRecoveryManager.RecoveredReason int reason,
            boolean isFirstValidation,
            int durationMillisOfCurrentAction) {
        if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
            phone = phone.getDefaultPhone();
        }

        int carrierId = phone.getCarrierId();
        int rat = getRat(phone);
        int band =
                (rat == TelephonyManager.NETWORK_TYPE_IWLAN) ? 0 : ServiceStateStats.getBand(phone);
        // the number returned here matches the SignalStrength enum we have
        int signalStrength = phone.getSignalStrength().getLevel();
        boolean isOpportunistic = getIsOpportunistic(phone);
        boolean isMultiSim = SimSlotState.getCurrentState().numActiveSims > 1;

        if (recoveryAction == DataStallRecoveryManager.RECOVERY_ACTION_RADIO_RESTART) {
            recoveryAction = RECOVERY_ACTION_RADIO_RESTART_MAPPING;
        } else if (recoveryAction == DataStallRecoveryManager.RECOVERY_ACTION_RESET_MODEM) {
            recoveryAction = RECOVERY_ACTION_RESET_MODEM_MAPPING;
        }

        // collect info of the other device in case of DSDS
        int otherSignalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
        // the number returned here matches the NetworkRegistrationState enum we have
        int otherNetworkRegState = NetworkRegistrationInfo
            int durationOfAction) {

        mHandler.post(() -> {
            // Update data stall stats
            log("Set recovery action to " + action);

            // Refreshes the metrics data.
            try {
                refreshMetricsData();
            } catch (Exception e) {
                loge("The metrics data cannot be refreshed.", e);
                return;
            }

            /**
             * TODO: Extend TelephonyStatsLog.write to upload the TCP information and data
             * networking stats data.
            */
            TelephonyStatsLog.write(
                    TelephonyStatsLog.DATA_STALL_RECOVERY_REPORTED,
                    mCarrierId,
                    mRat,
                    mSignalStrength,
                    action,
                    mIsOpportunistic,
                    mIsMultiSim,
                    mBand,
                    isRecovered,
                    duration,
                    reason,
                    mOtherSignalStrength,
                    mOtherNetworkRegState,
                    mNetworkRegState,
                    isFirstValidation,
                    mPhoneId,
                    durationOfAction);

            log("Upload stats: "
                    + "Action:"
                    + action
                    + ", Recovered:"
                    + isRecovered
                    + ", Duration:"
                    + duration
                    + ", Reason:"
                    + reason
                    + ", First validation:"
                    + isFirstValidation
                    + ", Duration of action:"
                    + durationOfAction
                    + ", "
                    + this);
        });
    }

    /**
     * Refreshes the metrics data.
     */
    private void refreshMetricsData() {
        logd("Refreshes the metrics data.");
        // Update phone id/carrier id and signal strength
        mPhoneId = mPhone.getPhoneId() + 1;
        mCarrierId = mPhone.getCarrierId();
        mSignalStrength = mPhone.getSignalStrength().getLevel();

        // Update the bandwidth.
        updateBandwidths();

        // Update the RAT and band.
        updateRatAndBand();

        // Update the opportunistic state.
        mIsOpportunistic = getIsOpportunistic(mPhone);

        // Update the multi-SIM state.
        mIsMultiSim = SimSlotState.getCurrentState().numActiveSims > 1;

        // Update the network registration state.
        updateNetworkRegState();

        // Update the DSDS information.
        updateDsdsInfo();
    }

    /**
     * Updates the bandwidth for the current data network.
     */
    private void updateBandwidths() {
        mLinkDownBandwidthKbps = mLinkUpBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;

        if (mIfaceName == null) {
            loge("Interface name is null");
            return;
        }

        DataNetworkController dataNetworkController = mPhone.getDataNetworkController();
        if (dataNetworkController == null) {
            loge("DataNetworkController is null");
            return;
        }

        DataNetwork dataNetwork = dataNetworkController.getDataNetworkByInterface(mIfaceName);
        if (dataNetwork == null) {
            loge("DataNetwork is null");
            return;
        }
        NetworkCapabilities networkCapabilities = dataNetwork.getNetworkCapabilities();
        if (networkCapabilities == null) {
            loge("NetworkCapabilities is null");
            return;
        }

        mLinkDownBandwidthKbps = networkCapabilities.getLinkDownstreamBandwidthKbps();
        mLinkUpBandwidthKbps = networkCapabilities.getLinkUpstreamBandwidthKbps();
    }

    private void updateRatAndBand() {
        mRat = TelephonyManager.NETWORK_TYPE_UNKNOWN;
        mBand = 0;
        ServiceState serviceState = mPhone.getServiceState();
        if (serviceState == null) {
            loge("ServiceState is null");
            return;
        }

        mRat = serviceState.getDataNetworkType();
        mBand =
            (mRat == TelephonyManager.NETWORK_TYPE_IWLAN) ? 0 : ServiceStateStats.getBand(mPhone);
    }

    private static boolean getIsOpportunistic(@NonNull Phone phone) {
        SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance()
                .getSubscriptionInfoInternal(phone.getSubId());
        return subInfo != null && subInfo.isOpportunistic();
    }

    private void updateNetworkRegState() {
        mNetworkRegState = NetworkRegistrationInfo
            .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;

        NetworkRegistrationInfo phoneRegInfo = mPhone.getServiceState()
                .getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS,
                AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
        if (phoneRegInfo != null) {
            mNetworkRegState = phoneRegInfo.getRegistrationState();
        }
    }

    private void updateDsdsInfo() {
        mOtherSignalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
        mOtherNetworkRegState = NetworkRegistrationInfo
            .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
        for (Phone otherPhone : PhoneFactory.getPhones()) {
            if (otherPhone.getPhoneId() == phone.getPhoneId()) continue;
            if (otherPhone.getPhoneId() == mPhone.getPhoneId()) continue;
            if (!getIsOpportunistic(otherPhone)) {
                otherSignalStrength = otherPhone.getSignalStrength().getLevel();
                mOtherSignalStrength = otherPhone.getSignalStrength().getLevel();
                NetworkRegistrationInfo regInfo = otherPhone.getServiceState()
                        .getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS,
                        AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
                if (regInfo != null) {
                    otherNetworkRegState = regInfo.getRegistrationState();
                    mOtherNetworkRegState = regInfo.getRegistrationState();
                }
                break;
            }
        }
    }

        // the number returned here matches the NetworkRegistrationState enum we have
        int phoneNetworkRegState = NetworkRegistrationInfo
                .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;

        NetworkRegistrationInfo phoneRegInfo = phone.getServiceState()
                        .getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS,
                                AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
        if (phoneRegInfo != null) {
            phoneNetworkRegState = phoneRegInfo.getRegistrationState();
    private void log(@NonNull String s) {
        Rlog.i(mTag, s);
    }

        // reserve 0 for default value
        int phoneId = phone.getPhoneId() + 1;
    private void logd(@NonNull String s) {
        Rlog.d(mTag, s);
    }

        TelephonyStatsLog.write(
                TelephonyStatsLog.DATA_STALL_RECOVERY_REPORTED,
                carrierId,
                rat,
                signalStrength,
                recoveryAction,
                isOpportunistic,
                isMultiSim,
                band,
                isRecovered,
                durationMillis,
                reason,
                otherSignalStrength,
                otherNetworkRegState,
                phoneNetworkRegState,
                isFirstValidation,
                phoneId,
                durationMillisOfCurrentAction);
    private void loge(@NonNull String s) {
        Rlog.e(mTag, s);
    }

    /** Returns the RAT used for data (including IWLAN). */
    private static @NetworkType int getRat(Phone phone) {
        ServiceStateTracker serviceStateTracker = phone.getServiceStateTracker();
        ServiceState serviceState =
                serviceStateTracker != null ? serviceStateTracker.getServiceState() : null;
        return serviceState != null
                ? serviceState.getDataNetworkType()
                : TelephonyManager.NETWORK_TYPE_UNKNOWN;
    private void loge(@NonNull String s, Throwable tr) {
        Rlog.e(mTag, s, tr);
    }

    private static boolean getIsOpportunistic(Phone phone) {
        SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance()
                .getSubscriptionInfoInternal(phone.getSubId());
        return subInfo != null && subInfo.isOpportunistic();
    @Override
    public String toString() {
        return "DataStallRecoveryStats {"
            + "Phone id:"
            + mPhoneId
            + ", Signal strength:"
            + mSignalStrength
            + ", Band:" + mBand
            + ", RAT:" + mRat
            + ", Opportunistic:"
            + mIsOpportunistic
            + ", Multi-SIM:"
            + mIsMultiSim
            + ", Network reg state:"
            + mNetworkRegState
            + ", Other signal strength:"
            + mOtherSignalStrength
            + ", Other network reg state:"
            + mOtherNetworkRegState
            + ", Link status:"
            + mInternetLinkStatus
            + ", Link down bandwidth:"
            + mLinkDownBandwidthKbps
            + ", Link up bandwidth:"
            + mLinkUpBandwidthKbps
            + "}";
    }
}
+4 −4
Original line number Diff line number Diff line
@@ -93,22 +93,22 @@ public class DataStallRecoveryManagerTest extends TelephonyTest {
    private void sendValidationStatusCallback(@ValidationStatus int status) {
        ArgumentCaptor<DataNetworkControllerCallback> dataNetworkControllerCallbackCaptor =
                ArgumentCaptor.forClass(DataNetworkControllerCallback.class);
        verify(mDataNetworkController)
        verify(mDataNetworkController, times(2))
                .registerDataNetworkControllerCallback(
                        dataNetworkControllerCallbackCaptor.capture());
        DataNetworkControllerCallback dataNetworkControllerCallback =
                dataNetworkControllerCallbackCaptor.getValue();
                dataNetworkControllerCallbackCaptor.getAllValues().get(0);
        dataNetworkControllerCallback.onInternetDataNetworkValidationStatusChanged(status);
    }

    private void sendOnInternetDataNetworkCallback(boolean isConnected) {
        ArgumentCaptor<DataNetworkControllerCallback> dataNetworkControllerCallbackCaptor =
                ArgumentCaptor.forClass(DataNetworkControllerCallback.class);
        verify(mDataNetworkController)
        verify(mDataNetworkController, times(2))
                .registerDataNetworkControllerCallback(
                        dataNetworkControllerCallbackCaptor.capture());
        DataNetworkControllerCallback dataNetworkControllerCallback =
                dataNetworkControllerCallbackCaptor.getValue();
                dataNetworkControllerCallbackCaptor.getAllValues().get(0);

        if (isConnected) {
            List<DataNetwork> dataprofile = new ArrayList<>();