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

Commit 911f3c6f authored by Mars Lin's avatar Mars Lin
Browse files

Collect connectivity diagnostics related information from DSRS

1. Collect validation and network probe results
2. Sending notification when data stall recovered

Bug: 319601607
Test: Successfully test on Pixel device with simulate data stall environment
Change-Id: Ifbbf20ac5f273fb5cd452f79a055b567bbfe04c6
parent f30c091f
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -118,3 +118,10 @@ flag {
  description: "This flag is for internal implementation to handle reconnect request from QNS in telephony FWK."
  bug: "319520561"
}

flag {
  name: "dsrs_diagnostics_enabled"
  namespace: "telephony"
  description: "Enable DSRS diagnostics."
  bug: "319601607"
}
 No newline at end of file
+27 −4
Original line number Diff line number Diff line
@@ -48,6 +48,8 @@ import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback;
import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback;
import com.android.internal.telephony.flags.FeatureFlags;
import com.android.internal.telephony.flags.FeatureFlagsImpl;
import com.android.internal.telephony.metrics.DataStallRecoveryStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.telephony.Rlog;
@@ -153,6 +155,7 @@ public class DataStallRecoveryManager extends Handler {
    private final @NonNull Phone mPhone;
    private final @NonNull String mLogTag;
    private final @NonNull LocalLog mLocalLog = new LocalLog(128);
    private final @NonNull FeatureFlags mFeatureFlags = new FeatureFlagsImpl();

    /** Data network controller */
    private final @NonNull DataNetworkController mDataNetworkController;
@@ -196,7 +199,10 @@ public class DataStallRecoveryManager extends Handler {
    private boolean mIsInternetNetworkConnected;
    /** The durations for current recovery action */
    private @ElapsedRealtimeLong long mTimeElapsedOfCurrentAction;

    /** Tracks the total number of validation duration a data stall */
    private int mValidationCount;
    /** Tracks the number of validation for current action during a data stall */
    private int mActionValidationCount;
    /** The array for the timers between recovery actions. */
    private @NonNull long[] mDataStallRecoveryDelayMillisArray;
    /** The boolean array for the flags. They are used to skip the recovery actions if needed. */
@@ -546,6 +552,8 @@ public class DataStallRecoveryManager extends Handler {
        mTimeLastRecoveryStartMs = 0;
        mLastAction = RECOVERY_ACTION_GET_DATA_CALL_LIST;
        mRecoveryAction = RECOVERY_ACTION_GET_DATA_CALL_LIST;
        mValidationCount = 0;
        mActionValidationCount = 0;
    }

    /**
@@ -556,8 +564,16 @@ public class DataStallRecoveryManager extends Handler {
    private void onInternetValidationStatusChanged(@ValidationStatus int status) {
        logl("onInternetValidationStatusChanged: " + DataUtils.validationStatusToString(status));
        final boolean isValid = status == NetworkAgent.VALIDATION_STATUS_VALID;
        if (mFeatureFlags.dsrsDiagnosticsEnabled()) {
            mValidationCount += 1;
            mActionValidationCount += 1;
        }
        setNetworkValidationState(isValid);
        if (isValid) {
            if (mFeatureFlags.dsrsDiagnosticsEnabled()) {
                // Broadcast intent that data stall recovered.
                broadcastDataStallDetected(getRecoveryAction());
            }
            reset();
        } else if (isRecoveryNeeded(true)) {
            // Set the network as invalid, because recovery is needed
@@ -596,6 +612,10 @@ public class DataStallRecoveryManager extends Handler {
     */
    @VisibleForTesting
    public void setRecoveryAction(@RecoveryAction int action) {
        // Reset the validation count for action change
        if (mFeatureFlags.dsrsDiagnosticsEnabled() && mRecoveryAction != action) {
            mActionValidationCount = 0;
        }
        mRecoveryAction = action;

        // Check if the mobile data enabled is TRUE, it means that the mobile data setting changed
@@ -674,13 +694,16 @@ public class DataStallRecoveryManager extends Handler {
        final boolean isRecovered = !mDataStalled;
        final int duration = (int) (SystemClock.elapsedRealtime() - mDataStallStartMs);
        final @RecoveredReason int reason = getRecoveredReason(mIsValidNetwork);
        final boolean isFirstValidationOfAction = false;
        final int durationOfAction = (int) getDurationOfCurrentRecoveryMs();
        if (mFeatureFlags.dsrsDiagnosticsEnabled()) {
            log("mValidationCount=" + mValidationCount
                    + ", mActionValidationCount=" + mActionValidationCount);
        }

        // Get the bundled DSRS stats.
        Bundle bundle = mStats.getDataStallRecoveryMetricsData(
                recoveryAction, isRecovered, duration, reason, isFirstValidationOfAction,
                durationOfAction);
                recoveryAction, isRecovered, duration, reason, mValidationCount,
                mActionValidationCount, durationOfAction);

        // Put the bundled stats extras on the intent.
        intent.putExtra("EXTRA_DSRS_STATS_BUNDLE", bundle);
+170 −21
Original line number Diff line number Diff line
@@ -16,12 +16,27 @@

package com.android.internal.telephony.metrics;

import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_ATTEMPTED_BITMASK;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_SUCCEEDED_BITMASK;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_VALIDATION_RESULT;
import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS;
import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_PACKET_FAIL_RATE;
import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_DNS_CONSECUTIVE_TIMEOUTS;

import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.ConnectivityDiagnosticsManager;
import android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback;
import android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
import android.net.ConnectivityDiagnosticsManager.DataStallReport;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.PersistableBundle;
import android.os.SystemClock;
import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation.NetworkType;
import android.telephony.CellSignalStrength;
@@ -30,6 +45,7 @@ import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.telephony.data.DataCallResponse;
import android.telephony.data.DataCallResponse.LinkStatus;
import android.text.TextUtils;

import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
@@ -38,11 +54,15 @@ 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.flags.FeatureFlags;
import com.android.internal.telephony.flags.FeatureFlagsImpl;
import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.telephony.Rlog;

import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;

/**
 * Generates metrics related to data stall recovery events per phone ID for the pushed atom.
@@ -57,18 +77,31 @@ public class DataStallRecoveryStats {

    private static final String TAG = "DSRS-";

    private static final int UNSET_DIAGNOSTIC_STATE = -1;

    private static final long REFRESH_DURATION_IN_MILLIS = TimeUnit.MINUTES.toMillis(3);

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

    private final @NonNull String mTag;
    private final @NonNull Phone mPhone;
    private final @NonNull TelephonyManager mTelephonyManager;
    private final @NonNull FeatureFlags mFeatureFlags = new FeatureFlagsImpl();

    // Flag to control the DSRS diagnostics
    private final boolean mIsDsrsDiagnosticsEnabled;

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

    /* Metrics and stats data variables */
    // Record metrics refresh time in milliseconds to decide whether to refresh data again
    @ElapsedRealtimeLong
    private long mMetricsReflashTime = 0L;
    private int mPhoneId = 0;
    private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
    private int mConvertedMccMnc = -1;
    private int mSignalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
    private int mBand = 0;
    // The RAT used for data (including IWLAN).
@@ -88,6 +121,18 @@ public class DataStallRecoveryStats {
    private int mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
    private int mLinkUpBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;

    // Connectivity diagnostics states
    private int mNetworkProbesResult = UNSET_DIAGNOSTIC_STATE;
    private int mNetworkProbesType = UNSET_DIAGNOSTIC_STATE;
    private int mNetworkValidationResult = UNSET_DIAGNOSTIC_STATE;
    private int mTcpMetricsCollectionPeriodMillis = UNSET_DIAGNOSTIC_STATE;
    private int mTcpPacketFailRate = UNSET_DIAGNOSTIC_STATE;
    private int mDnsConsecutiveTimeouts = UNSET_DIAGNOSTIC_STATE;

    private ConnectivityDiagnosticsManager mConnectivityDiagnosticsManager = null;
    private ConnectivityDiagnosticsCallback mConnectivityDiagnosticsCallback = null;
    private static final Executor INLINE_EXECUTOR = x -> x.run();

    /**
     * Constructs a new instance of {@link DataStallRecoveryStats}.
     */
@@ -99,6 +144,7 @@ public class DataStallRecoveryStats {
        HandlerThread handlerThread = new HandlerThread(mTag + "-thread");
        handlerThread.start();
        mHandler = new Handler(handlerThread.getLooper());
        mTelephonyManager = mPhone.getContext().getSystemService(TelephonyManager.class);

        dataNetworkController.registerDataNetworkControllerCallback(
                new DataNetworkControllerCallback(mHandler::post) {
@@ -117,6 +163,45 @@ public class DataStallRecoveryStats {
                    mInternetLinkStatus = status;
                }
            });

        mIsDsrsDiagnosticsEnabled = mFeatureFlags.dsrsDiagnosticsEnabled();
        if (mIsDsrsDiagnosticsEnabled) {
            try {
                // Register ConnectivityDiagnosticsCallback to get diagnostics states
                mConnectivityDiagnosticsManager =
                    mPhone.getContext().getSystemService(ConnectivityDiagnosticsManager.class);
                mConnectivityDiagnosticsCallback = new ConnectivityDiagnosticsCallback() {
                    @Override
                    public void onConnectivityReportAvailable(@NonNull ConnectivityReport report) {
                        PersistableBundle bundle = report.getAdditionalInfo();
                        mNetworkProbesResult = bundle.getInt(KEY_NETWORK_PROBES_SUCCEEDED_BITMASK);
                        mNetworkProbesType = bundle.getInt(KEY_NETWORK_PROBES_ATTEMPTED_BITMASK);
                        mNetworkValidationResult = bundle.getInt(KEY_NETWORK_VALIDATION_RESULT);
                    }

                    @Override
                    public void onDataStallSuspected(@NonNull DataStallReport report) {
                        PersistableBundle bundle = report.getStallDetails();
                        mTcpMetricsCollectionPeriodMillis =
                            bundle.getInt(KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS);
                        mTcpPacketFailRate = bundle.getInt(KEY_TCP_PACKET_FAIL_RATE);
                        mDnsConsecutiveTimeouts = bundle.getInt(KEY_DNS_CONSECUTIVE_TIMEOUTS);
                    }
                };
                mConnectivityDiagnosticsManager.registerConnectivityDiagnosticsCallback(
                    new NetworkRequest.Builder()
                        .clearCapabilities()
                        .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                        .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                        .build(),
                        INLINE_EXECUTOR,
                        mConnectivityDiagnosticsCallback
                );
            } catch (Exception e) {
                mConnectivityDiagnosticsManager = null;
                mConnectivityDiagnosticsCallback = null;
            }
        }
    }

    /**
@@ -194,10 +279,26 @@ public class DataStallRecoveryStats {
     */
    private void refreshMetricsData() {
        logd("Refreshes the metrics data.");
        // Update the metrics reflash time
        mMetricsReflashTime = SystemClock.elapsedRealtime();
        // Update phone id/carrier id and signal strength
        mPhoneId = mPhone.getPhoneId() + 1;
        mCarrierId = mPhone.getCarrierId();
        mSignalStrength = mPhone.getSignalStrength().getLevel();
        if (mIsDsrsDiagnosticsEnabled) {
            // Get the MCCMNC and convert it to an int
            String networkOperator = mTelephonyManager.getNetworkOperator();
            if (!TextUtils.isEmpty(networkOperator)) {
                try {
                    mConvertedMccMnc = Integer.parseInt(networkOperator);
                } catch (NumberFormatException e) {
                    loge("Invalid MCCMNC format: " + networkOperator);
                    mConvertedMccMnc = -1;
                }
            } else {
                mConvertedMccMnc = -1;
            }
        }

        // Update the bandwidth.
        updateBandwidths();
@@ -308,7 +409,8 @@ public class DataStallRecoveryStats {
     * @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 validationCount The total number of validation duration a data stall.
     * @param actionValidationCount The number of validation for current action during a data stall
     * @param durationOfAction The duration of the current action in milliseconds.
     */
    public Bundle getDataStallRecoveryMetricsData(
@@ -316,14 +418,59 @@ public class DataStallRecoveryStats {
            boolean isRecovered,
            int duration,
            @DataStallRecoveryManager.RecoveredReason int reason,
            boolean isFirstValidation,
            int validationCount,
            int actionValidationCount,
            int durationOfAction) {

        if (mIsDsrsDiagnosticsEnabled) {
            // Refresh data if the data has not been updated within 3 minutes
            final long refreshDuration = SystemClock.elapsedRealtime() - mMetricsReflashTime;
            if (refreshDuration > REFRESH_DURATION_IN_MILLIS) {
                // Refreshes the metrics data.
                try {
                    refreshMetricsData();
                } catch (Exception e) {
                    loge("The metrics data cannot be refreshed.", e);
                }
            }
        }

        Bundle bundle = new Bundle();

        if (mIsDsrsDiagnosticsEnabled) {
            bundle.putInt("Action", action);
            bundle.putInt("IsRecovered", isRecovered ? 1 : 0);
            bundle.putInt("Duration", duration);
            bundle.putInt("Reason", reason);
            bundle.putInt("DurationOfAction", durationOfAction);
            bundle.putInt("ValidationCount", validationCount);
            bundle.putInt("ActionValidationCount", actionValidationCount);
            bundle.putInt("PhoneId", mPhoneId);
            bundle.putInt("CarrierId", mCarrierId);
            bundle.putInt("MccMnc", mConvertedMccMnc);
            bundle.putInt("SignalStrength", mSignalStrength);
            bundle.putInt("Band", mBand);
            bundle.putInt("Rat", mRat);
            bundle.putInt("IsOpportunistic", mIsOpportunistic ? 1 : 0);
            bundle.putInt("IsMultiSim", mIsMultiSim ? 1 : 0);
            bundle.putInt("NetworkRegState", mNetworkRegState);
            bundle.putInt("OtherSignalStrength", mOtherSignalStrength);
            bundle.putInt("OtherNetworkRegState", mOtherNetworkRegState);
            bundle.putInt("InternetLinkStatus", mInternetLinkStatus);
            bundle.putInt("LinkDownBandwidthKbps", mLinkDownBandwidthKbps);
            bundle.putInt("LinkUpBandwidthKbps", mLinkUpBandwidthKbps);
            bundle.putInt("NetworkProbesResult", mNetworkProbesResult);
            bundle.putInt("NetworkProbesType", mNetworkProbesType);
            bundle.putInt("NetworkValidationResult", mNetworkValidationResult);
            bundle.putInt("TcpMetricsCollectionPeriodMillis", mTcpMetricsCollectionPeriodMillis);
            bundle.putInt("TcpPacketFailRate", mTcpPacketFailRate);
            bundle.putInt("DnsConsecutiveTimeouts", mDnsConsecutiveTimeouts);
        } else {
            bundle.putInt("Action", action);
            bundle.putBoolean("IsRecovered", isRecovered);
            bundle.putInt("Duration", duration);
            bundle.putInt("Reason", reason);
        bundle.putBoolean("IsFirstValidation", isFirstValidation);
            bundle.putBoolean("IsFirstValidation", validationCount == 1);
            bundle.putInt("DurationOfAction", durationOfAction);
            bundle.putInt("PhoneId", mPhoneId);
            bundle.putInt("CarrierId", mCarrierId);
@@ -338,6 +485,8 @@ public class DataStallRecoveryStats {
            bundle.putInt("InternetLinkStatus", mInternetLinkStatus);
            bundle.putInt("LinkDownBandwidthKbps", mLinkDownBandwidthKbps);
            bundle.putInt("LinkUpBandwidthKbps", mLinkUpBandwidthKbps);
        }

        return bundle;
    }

+10 −2
Original line number Diff line number Diff line
@@ -58,6 +58,8 @@ import java.util.Set;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class DataStallRecoveryManagerTest extends TelephonyTest {
    private static final String KEY_IS_DSRS_DIAGNOSTICS_ENABLED =
            "is_dsrs_diagnostics_enabled";
    private FakeContentResolver mFakeContentResolver;

    // Mocked classes
@@ -429,6 +431,7 @@ public class DataStallRecoveryManagerTest extends TelephonyTest {
    @Test
    public void testSendDSRMData() throws Exception {
        ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
        boolean isDsrsDiagnosticsEnabled = mFeatureFlags.dsrsDiagnosticsEnabled();

        logd("Set phone status to normal status.");
        sendOnInternetDataNetworkCallback(true);
@@ -460,10 +463,15 @@ public class DataStallRecoveryManagerTest extends TelephonyTest {
            logd(bundle.toString());
            int size = bundle.size();
            logd("bundle size is " + size);
            if (isDsrsDiagnosticsEnabled) {
                // Check if bundle size is 27
                assertThat(size).isEqualTo(27);
            } else {
                // Check if bundle size is 19
                assertThat(size).isEqualTo(19);
            }
        }
    }

    /**
     * Tests update action enable state and duration from global settings.