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
diff --git a/flags/data.aconfig b/flags/data.aconfig
index 3beb016..a265260 100644
--- a/flags/data.aconfig
+++ b/flags/data.aconfig
@@ -117,4 +117,11 @@
namespace: "telephony"
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
diff --git a/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java b/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
index 893509c..e9c00d9 100644
--- a/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
+++ b/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
@@ -48,6 +48,8 @@
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 @@
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 @@
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 @@
mTimeLastRecoveryStartMs = 0;
mLastAction = RECOVERY_ACTION_GET_DATA_CALL_LIST;
mRecoveryAction = RECOVERY_ACTION_GET_DATA_CALL_LIST;
+ mValidationCount = 0;
+ mActionValidationCount = 0;
}
/**
@@ -556,8 +564,16 @@
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 @@
*/
@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 @@
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);
diff --git a/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java b/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
index 387495e..c34c559 100644
--- a/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
+++ b/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
@@ -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.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.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 @@
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 @@
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 @@
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 @@
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 @@
*/
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 @@
* @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,28 +418,75 @@
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();
- bundle.putInt("Action", action);
- bundle.putBoolean("IsRecovered", isRecovered);
- bundle.putInt("Duration", duration);
- bundle.putInt("Reason", reason);
- bundle.putBoolean("IsFirstValidation", isFirstValidation);
- bundle.putInt("DurationOfAction", durationOfAction);
- bundle.putInt("PhoneId", mPhoneId);
- bundle.putInt("CarrierId", mCarrierId);
- bundle.putInt("SignalStrength", mSignalStrength);
- bundle.putInt("Band", mBand);
- bundle.putInt("Rat", mRat);
- bundle.putBoolean("IsOpportunistic", mIsOpportunistic);
- bundle.putBoolean("IsMultiSim", mIsMultiSim);
- bundle.putInt("NetworkRegState", mNetworkRegState);
- bundle.putInt("OtherSignalStrength", mOtherSignalStrength);
- bundle.putInt("OtherNetworkRegState", mOtherNetworkRegState);
- bundle.putInt("InternetLinkStatus", mInternetLinkStatus);
- bundle.putInt("LinkDownBandwidthKbps", mLinkDownBandwidthKbps);
- bundle.putInt("LinkUpBandwidthKbps", mLinkUpBandwidthKbps);
+
+ 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", validationCount == 1);
+ bundle.putInt("DurationOfAction", durationOfAction);
+ bundle.putInt("PhoneId", mPhoneId);
+ bundle.putInt("CarrierId", mCarrierId);
+ bundle.putInt("SignalStrength", mSignalStrength);
+ bundle.putInt("Band", mBand);
+ bundle.putInt("Rat", mRat);
+ bundle.putBoolean("IsOpportunistic", mIsOpportunistic);
+ bundle.putBoolean("IsMultiSim", mIsMultiSim);
+ bundle.putInt("NetworkRegState", mNetworkRegState);
+ bundle.putInt("OtherSignalStrength", mOtherSignalStrength);
+ bundle.putInt("OtherNetworkRegState", mOtherNetworkRegState);
+ bundle.putInt("InternetLinkStatus", mInternetLinkStatus);
+ bundle.putInt("LinkDownBandwidthKbps", mLinkDownBandwidthKbps);
+ bundle.putInt("LinkUpBandwidthKbps", mLinkUpBandwidthKbps);
+ }
+
return bundle;
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java
index e508e5b..18efce5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java
@@ -58,6 +58,8 @@
@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 @@
@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,8 +463,13 @@
logd(bundle.toString());
int size = bundle.size();
logd("bundle size is " + size);
- // Check if bundle size is 19
- assertThat(size).isEqualTo(19);
+ if (isDsrsDiagnosticsEnabled) {
+ // Check if bundle size is 27
+ assertThat(size).isEqualTo(27);
+ } else {
+ // Check if bundle size is 19
+ assertThat(size).isEqualTo(19);
+ }
}
}