Loading flags/data.aconfig +7 −0 Original line number Diff line number Diff line Loading @@ -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 src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java +27 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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. */ Loading Loading @@ -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; } /** Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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); Loading src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java +170 −21 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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. Loading @@ -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). Loading @@ -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}. */ Loading @@ -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) { Loading @@ -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; } } } /** Loading Loading @@ -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(); Loading Loading @@ -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( Loading @@ -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); Loading @@ -338,6 +485,8 @@ public class DataStallRecoveryStats { bundle.putInt("InternetLinkStatus", mInternetLinkStatus); bundle.putInt("LinkDownBandwidthKbps", mLinkDownBandwidthKbps); bundle.putInt("LinkUpBandwidthKbps", mLinkUpBandwidthKbps); } return bundle; } Loading tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java +10 −2 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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); Loading Loading @@ -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. Loading Loading
flags/data.aconfig +7 −0 Original line number Diff line number Diff line Loading @@ -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
src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java +27 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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. */ Loading Loading @@ -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; } /** Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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); Loading
src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java +170 −21 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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. Loading @@ -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). Loading @@ -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}. */ Loading @@ -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) { Loading @@ -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; } } } /** Loading Loading @@ -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(); Loading Loading @@ -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( Loading @@ -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); Loading @@ -338,6 +485,8 @@ public class DataStallRecoveryStats { bundle.putInt("InternetLinkStatus", mInternetLinkStatus); bundle.putInt("LinkDownBandwidthKbps", mLinkDownBandwidthKbps); bundle.putInt("LinkUpBandwidthKbps", mLinkUpBandwidthKbps); } return bundle; } Loading
tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java +10 −2 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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); Loading Loading @@ -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. Loading