Loading src/java/com/android/internal/telephony/data/AccessNetworksManager.java +78 −4 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.internal.telephony.data; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; import android.content.BroadcastReceiver; import android.content.ComponentName; Loading Loading @@ -56,6 +57,7 @@ import android.util.SparseArray; import com.android.internal.telephony.Phone; import com.android.internal.telephony.RIL; import com.android.internal.telephony.SlidingWindowEventCounter; import com.android.internal.telephony.dataconnection.DataThrottler; import com.android.telephony.Rlog; Loading Loading @@ -111,6 +113,11 @@ public class AccessNetworksManager extends Handler { */ public static final String IWLAN_OPERATION_MODE_AP_ASSISTED = "AP-assisted"; /** * The counters to detect frequent QNS attempt to change preferred network transport by ApnType. */ private final @NonNull SparseArray<SlidingWindowEventCounter> mApnTypeToQnsChangeNetworkCounter; private final String mLogTag; private final LocalLog mLocalLog = new LocalLog(64); private final UUID mAnomalyUUID = UUID.fromString("c2d1a639-00e2-4561-9619-6acf37d90590"); Loading @@ -131,6 +138,8 @@ public class AccessNetworksManager extends Handler { private final CarrierConfigManager mCarrierConfigManager; private @Nullable DataConfigManager mDataConfigManager; private IQualifiedNetworksService mIQualifiedNetworksService; private AccessNetworksManagerDeathRecipient mDeathRecipient; Loading Loading @@ -344,12 +353,24 @@ public class AccessNetworksManager extends Handler { if (Arrays.stream(qualifiedNetworkTypes).anyMatch(accessNetwork -> !DataUtils.isValidAccessNetwork(accessNetwork))) { loge("Invalid access networks " + Arrays.toString(qualifiedNetworkTypes)); if (mDataConfigManager != null && mDataConfigManager.isInvalidQnsParamAnomalyReportEnabled()) { reportAnomaly("QNS requested invalid Network Type", "3e89a3df-3524-45fa-b5f2-0fb0e4c77ec4"); } return; } List<QualifiedNetworks> qualifiedNetworksList = new ArrayList<>(); int satisfiedApnTypes = 0; for (int apnType : SUPPORTED_APN_TYPES) { if ((apnTypes & apnType) == apnType) { // skip the APN anomaly detection if not using the T data stack if (mDataConfigManager != null) { satisfiedApnTypes |= apnType; trackFrequentApnTypeChange(apnType); } if (mAvailableNetworks.get(apnType) != null) { if (Arrays.equals(mAvailableNetworks.get(apnType), qualifiedNetworkTypes)) { Loading Loading @@ -382,6 +403,16 @@ public class AccessNetworksManager extends Handler { } } // Report anomaly if any requested APN types are unsatisfied if (satisfiedApnTypes != apnTypes && mDataConfigManager != null && mDataConfigManager.isInvalidQnsParamAnomalyReportEnabled()) { int unsatisfied = satisfiedApnTypes ^ apnTypes; reportAnomaly("QNS requested unsupported APN Types:" + Integer.toBinaryString(unsatisfied), "3e89a3df-3524-45fa-b5f2-0fb0e4c77ec4"); } if (!qualifiedNetworksList.isEmpty()) { setPreferredTransports(qualifiedNetworksList); mQualifiedNetworksChangedRegistrants.notifyResult(qualifiedNetworksList); Loading @@ -389,6 +420,27 @@ public class AccessNetworksManager extends Handler { } } /** * Called when receiving preferred transport change request for a specific apnType. * * @param apnType The requested apnType. */ private void trackFrequentApnTypeChange(@ApnSetting.ApnType int apnType) { SlidingWindowEventCounter counter = mApnTypeToQnsChangeNetworkCounter.get(apnType); if (counter == null) { counter = new SlidingWindowEventCounter( mDataConfigManager.getAnomalyQnsChangeThreshold().timeWindow, mDataConfigManager.getAnomalyQnsChangeThreshold().eventNumOccurrence); mApnTypeToQnsChangeNetworkCounter.put(apnType, counter); } if (counter.addOccurrence()) { reportAnomaly("QNS requested network change for " + ApnSetting.getApnTypeString(apnType) + " " + counter.getFrequencyString(), "3e89a3df-3524-45fa-b5f2-b8911abc7d57"); } } /** * Access networks manager callback. This should be only used by {@link DataNetworkController}. */ Loading Loading @@ -422,6 +474,7 @@ public class AccessNetworksManager extends Handler { mCarrierConfigManager = (CarrierConfigManager) phone.getContext().getSystemService( Context.CARRIER_CONFIG_SERVICE); mLogTag = "ANM-" + mPhone.getPhoneId(); mApnTypeToQnsChangeNetworkCounter = new SparseArray<>(); if (isInLegacyMode()) { log("operates in legacy mode."); Loading @@ -446,9 +499,10 @@ public class AccessNetworksManager extends Handler { } if (phone.isUsingNewDataStack()) { // Using post to delay the registering because data retry manager instance is created // later than access networks manager. post(() -> mPhone.getDataNetworkController().getDataRetryManager().registerCallback( // Using post to delay the registering because data retry manager and data config // manager instances are created later than access networks manager. post(() -> { mPhone.getDataNetworkController().getDataRetryManager().registerCallback( new DataRetryManager.DataRetryManagerCallback(this::post) { @Override public void onThrottleStatusChanged(List<ThrottleStatus> throttleStatuses) { Loading @@ -462,8 +516,28 @@ public class AccessNetworksManager extends Handler { loge("onThrottleStatusChanged: ", ex); } } })); }); mDataConfigManager = mPhone.getDataNetworkController().getDataConfigManager(); mDataConfigManager.registerCallback( new DataConfigManager.DataConfigManagerCallback(this::post) { @Override public void onDeviceConfigChanged() { mApnTypeToQnsChangeNetworkCounter.clear(); } }); }); } } /** * Trigger the anomaly report with the specified UUID. * * @param anomalyMsg Description of the event * @param uuid UUID associated with that event */ private void reportAnomaly(@NonNull String anomalyMsg, @NonNull String uuid) { logl(anomalyMsg); AnomalyReporter.reportAnomaly(UUID.fromString(uuid), anomalyMsg, mPhone.getCarrierId()); } /** Loading src/java/com/android/internal/telephony/data/DataConfigManager.java +39 −9 Original line number Diff line number Diff line Loading @@ -205,6 +205,10 @@ public class DataConfigManager extends Handler { "anomaly_setup_data_call_failure"; /** DeviceConfig key of anomaly report threshold for frequent network-unwanted call. */ private static final String KEY_ANOMALY_NETWORK_UNWANTED = "anomaly_network_unwanted"; /** DeviceConfig key of anomaly report threshold for frequent change of preferred network. */ private static final String KEY_ANOMALY_QNS_CHANGE_NETWORK = "anomaly_qns_change_network"; /** DeviceConfig key of anomaly report threshold for invalid QNS params. */ private static final String KEY_ANOMALY_QNS_PARAM = "anomaly_qns_param"; /** DeviceConfig key of anomaly report threshold for DataNetwork stuck in connecting state. */ private static final String KEY_ANOMALY_NETWORK_CONNECTING_TIMEOUT = "anomaly_network_connecting_timeout"; Loading @@ -227,6 +231,17 @@ public class DataConfigManager extends Handler { */ private EventFrequency mNetworkUnwantedAnomalyReportThreshold; /** * Anomaly report thresholds for frequent APN type change at {@link AccessNetworksManager} */ private EventFrequency mQnsFrequentApnTypeChangeAnomalyReportThreshold; /** * {@code true} if enabled anomaly detection for param when QNS wants to change preferred * network at {@link AccessNetworksManager}. */ private boolean mIsInvalidQnsParamAnomalyReportEnabled; /** * Timeout in ms before creating an anomaly report for a DataNetwork stuck in * {@link DataNetwork.ConnectingState}. Loading Loading @@ -392,17 +407,15 @@ public class DataConfigManager extends Handler { DeviceConfig.getProperties(DeviceConfig.NAMESPACE_TELEPHONY); mImsReleaseRequestAnomalyReportThreshold = parseSlidingWindowCounterThreshold( properties.getString(KEY_ANOMALY_IMS_RELEASE_REQUEST, null), 0, 2); properties.getString(KEY_ANOMALY_IMS_RELEASE_REQUEST, null), 0, 2); mNetworkUnwantedAnomalyReportThreshold = parseSlidingWindowCounterThreshold( properties.getString(KEY_ANOMALY_NETWORK_UNWANTED, null), 0, 12); properties.getString(KEY_ANOMALY_NETWORK_UNWANTED, null), 0, 12); mSetupDataCallAnomalyReportThreshold = parseSlidingWindowCounterThreshold( properties.getString(KEY_ANOMALY_SETUP_DATA_CALL_FAILURE, null), 0, 12); properties.getString(KEY_ANOMALY_SETUP_DATA_CALL_FAILURE, null), 0, 12); mQnsFrequentApnTypeChangeAnomalyReportThreshold = parseSlidingWindowCounterThreshold( properties.getString(KEY_ANOMALY_QNS_CHANGE_NETWORK, null), 0, 5); mIsInvalidQnsParamAnomalyReportEnabled = properties.getBoolean( KEY_ANOMALY_QNS_PARAM, false); mNetworkConnectingTimeout = properties.getInt( KEY_ANOMALY_NETWORK_CONNECTING_TIMEOUT, DEFAULT_NETWORK_TRANSIT_STATE_TIMEOUT_MS); mNetworkDisconnectingTimeout = properties.getInt( Loading Loading @@ -829,6 +842,23 @@ public class DataConfigManager extends Handler { return mImsReleaseRequestAnomalyReportThreshold; } /** * Anomaly report thresholds for frequent QNS change of preferred network * at {@link AccessNetworksManager} * @return EventFrequency to trigger the anomaly report */ public @NonNull EventFrequency getAnomalyQnsChangeThreshold() { return mQnsFrequentApnTypeChangeAnomalyReportThreshold; } /** * @return {@code true} if enabled anomaly report for invalid param when QNS wants to change * preferred network at {@link AccessNetworksManager}. */ public boolean isInvalidQnsParamAnomalyReportEnabled() { return mIsInvalidQnsParamAnomalyReportEnabled; } /** * @return Timeout in ms before creating an anomaly report for a DataNetwork stuck in * {@link DataNetwork.ConnectingState}. Loading tests/telephonytests/src/com/android/internal/telephony/data/AccessNetworksManagerTest.java +7 −0 Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ public class AccessNetworksManagerTest extends TelephonyTest { private IQualifiedNetworksService mMockedQns; private IBinder mMockedIBinder; private AccessNetworksManagerCallback mMockedCallback; private DataConfigManager mMockedDataConfigManager; // The real callback passed created by AccessNetworksManager. private IQualifiedNetworksServiceCallback.Stub mQnsCallback; Loading Loading @@ -106,8 +107,14 @@ public class AccessNetworksManagerTest extends TelephonyTest { return null; }).when(mMockedCallback).invokeFromExecutor(any(Runnable.class)); mMockedDataConfigManager = Mockito.mock(DataConfigManager.class); doReturn(new DataConfigManager.EventFrequency(0, 2)) .when(mMockedDataConfigManager).getAnomalyQnsChangeThreshold(); mAccessNetworksManager = new AccessNetworksManager(mPhone, Looper.myLooper()); processAllMessages(); replaceInstance(AccessNetworksManager.class, "mDataConfigManager", mAccessNetworksManager, mMockedDataConfigManager); assumeFalse(mAccessNetworksManager.isInLegacyMode()); logd("-setUp"); } Loading Loading
src/java/com/android/internal/telephony/data/AccessNetworksManager.java +78 −4 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.internal.telephony.data; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; import android.content.BroadcastReceiver; import android.content.ComponentName; Loading Loading @@ -56,6 +57,7 @@ import android.util.SparseArray; import com.android.internal.telephony.Phone; import com.android.internal.telephony.RIL; import com.android.internal.telephony.SlidingWindowEventCounter; import com.android.internal.telephony.dataconnection.DataThrottler; import com.android.telephony.Rlog; Loading Loading @@ -111,6 +113,11 @@ public class AccessNetworksManager extends Handler { */ public static final String IWLAN_OPERATION_MODE_AP_ASSISTED = "AP-assisted"; /** * The counters to detect frequent QNS attempt to change preferred network transport by ApnType. */ private final @NonNull SparseArray<SlidingWindowEventCounter> mApnTypeToQnsChangeNetworkCounter; private final String mLogTag; private final LocalLog mLocalLog = new LocalLog(64); private final UUID mAnomalyUUID = UUID.fromString("c2d1a639-00e2-4561-9619-6acf37d90590"); Loading @@ -131,6 +138,8 @@ public class AccessNetworksManager extends Handler { private final CarrierConfigManager mCarrierConfigManager; private @Nullable DataConfigManager mDataConfigManager; private IQualifiedNetworksService mIQualifiedNetworksService; private AccessNetworksManagerDeathRecipient mDeathRecipient; Loading Loading @@ -344,12 +353,24 @@ public class AccessNetworksManager extends Handler { if (Arrays.stream(qualifiedNetworkTypes).anyMatch(accessNetwork -> !DataUtils.isValidAccessNetwork(accessNetwork))) { loge("Invalid access networks " + Arrays.toString(qualifiedNetworkTypes)); if (mDataConfigManager != null && mDataConfigManager.isInvalidQnsParamAnomalyReportEnabled()) { reportAnomaly("QNS requested invalid Network Type", "3e89a3df-3524-45fa-b5f2-0fb0e4c77ec4"); } return; } List<QualifiedNetworks> qualifiedNetworksList = new ArrayList<>(); int satisfiedApnTypes = 0; for (int apnType : SUPPORTED_APN_TYPES) { if ((apnTypes & apnType) == apnType) { // skip the APN anomaly detection if not using the T data stack if (mDataConfigManager != null) { satisfiedApnTypes |= apnType; trackFrequentApnTypeChange(apnType); } if (mAvailableNetworks.get(apnType) != null) { if (Arrays.equals(mAvailableNetworks.get(apnType), qualifiedNetworkTypes)) { Loading Loading @@ -382,6 +403,16 @@ public class AccessNetworksManager extends Handler { } } // Report anomaly if any requested APN types are unsatisfied if (satisfiedApnTypes != apnTypes && mDataConfigManager != null && mDataConfigManager.isInvalidQnsParamAnomalyReportEnabled()) { int unsatisfied = satisfiedApnTypes ^ apnTypes; reportAnomaly("QNS requested unsupported APN Types:" + Integer.toBinaryString(unsatisfied), "3e89a3df-3524-45fa-b5f2-0fb0e4c77ec4"); } if (!qualifiedNetworksList.isEmpty()) { setPreferredTransports(qualifiedNetworksList); mQualifiedNetworksChangedRegistrants.notifyResult(qualifiedNetworksList); Loading @@ -389,6 +420,27 @@ public class AccessNetworksManager extends Handler { } } /** * Called when receiving preferred transport change request for a specific apnType. * * @param apnType The requested apnType. */ private void trackFrequentApnTypeChange(@ApnSetting.ApnType int apnType) { SlidingWindowEventCounter counter = mApnTypeToQnsChangeNetworkCounter.get(apnType); if (counter == null) { counter = new SlidingWindowEventCounter( mDataConfigManager.getAnomalyQnsChangeThreshold().timeWindow, mDataConfigManager.getAnomalyQnsChangeThreshold().eventNumOccurrence); mApnTypeToQnsChangeNetworkCounter.put(apnType, counter); } if (counter.addOccurrence()) { reportAnomaly("QNS requested network change for " + ApnSetting.getApnTypeString(apnType) + " " + counter.getFrequencyString(), "3e89a3df-3524-45fa-b5f2-b8911abc7d57"); } } /** * Access networks manager callback. This should be only used by {@link DataNetworkController}. */ Loading Loading @@ -422,6 +474,7 @@ public class AccessNetworksManager extends Handler { mCarrierConfigManager = (CarrierConfigManager) phone.getContext().getSystemService( Context.CARRIER_CONFIG_SERVICE); mLogTag = "ANM-" + mPhone.getPhoneId(); mApnTypeToQnsChangeNetworkCounter = new SparseArray<>(); if (isInLegacyMode()) { log("operates in legacy mode."); Loading @@ -446,9 +499,10 @@ public class AccessNetworksManager extends Handler { } if (phone.isUsingNewDataStack()) { // Using post to delay the registering because data retry manager instance is created // later than access networks manager. post(() -> mPhone.getDataNetworkController().getDataRetryManager().registerCallback( // Using post to delay the registering because data retry manager and data config // manager instances are created later than access networks manager. post(() -> { mPhone.getDataNetworkController().getDataRetryManager().registerCallback( new DataRetryManager.DataRetryManagerCallback(this::post) { @Override public void onThrottleStatusChanged(List<ThrottleStatus> throttleStatuses) { Loading @@ -462,8 +516,28 @@ public class AccessNetworksManager extends Handler { loge("onThrottleStatusChanged: ", ex); } } })); }); mDataConfigManager = mPhone.getDataNetworkController().getDataConfigManager(); mDataConfigManager.registerCallback( new DataConfigManager.DataConfigManagerCallback(this::post) { @Override public void onDeviceConfigChanged() { mApnTypeToQnsChangeNetworkCounter.clear(); } }); }); } } /** * Trigger the anomaly report with the specified UUID. * * @param anomalyMsg Description of the event * @param uuid UUID associated with that event */ private void reportAnomaly(@NonNull String anomalyMsg, @NonNull String uuid) { logl(anomalyMsg); AnomalyReporter.reportAnomaly(UUID.fromString(uuid), anomalyMsg, mPhone.getCarrierId()); } /** Loading
src/java/com/android/internal/telephony/data/DataConfigManager.java +39 −9 Original line number Diff line number Diff line Loading @@ -205,6 +205,10 @@ public class DataConfigManager extends Handler { "anomaly_setup_data_call_failure"; /** DeviceConfig key of anomaly report threshold for frequent network-unwanted call. */ private static final String KEY_ANOMALY_NETWORK_UNWANTED = "anomaly_network_unwanted"; /** DeviceConfig key of anomaly report threshold for frequent change of preferred network. */ private static final String KEY_ANOMALY_QNS_CHANGE_NETWORK = "anomaly_qns_change_network"; /** DeviceConfig key of anomaly report threshold for invalid QNS params. */ private static final String KEY_ANOMALY_QNS_PARAM = "anomaly_qns_param"; /** DeviceConfig key of anomaly report threshold for DataNetwork stuck in connecting state. */ private static final String KEY_ANOMALY_NETWORK_CONNECTING_TIMEOUT = "anomaly_network_connecting_timeout"; Loading @@ -227,6 +231,17 @@ public class DataConfigManager extends Handler { */ private EventFrequency mNetworkUnwantedAnomalyReportThreshold; /** * Anomaly report thresholds for frequent APN type change at {@link AccessNetworksManager} */ private EventFrequency mQnsFrequentApnTypeChangeAnomalyReportThreshold; /** * {@code true} if enabled anomaly detection for param when QNS wants to change preferred * network at {@link AccessNetworksManager}. */ private boolean mIsInvalidQnsParamAnomalyReportEnabled; /** * Timeout in ms before creating an anomaly report for a DataNetwork stuck in * {@link DataNetwork.ConnectingState}. Loading Loading @@ -392,17 +407,15 @@ public class DataConfigManager extends Handler { DeviceConfig.getProperties(DeviceConfig.NAMESPACE_TELEPHONY); mImsReleaseRequestAnomalyReportThreshold = parseSlidingWindowCounterThreshold( properties.getString(KEY_ANOMALY_IMS_RELEASE_REQUEST, null), 0, 2); properties.getString(KEY_ANOMALY_IMS_RELEASE_REQUEST, null), 0, 2); mNetworkUnwantedAnomalyReportThreshold = parseSlidingWindowCounterThreshold( properties.getString(KEY_ANOMALY_NETWORK_UNWANTED, null), 0, 12); properties.getString(KEY_ANOMALY_NETWORK_UNWANTED, null), 0, 12); mSetupDataCallAnomalyReportThreshold = parseSlidingWindowCounterThreshold( properties.getString(KEY_ANOMALY_SETUP_DATA_CALL_FAILURE, null), 0, 12); properties.getString(KEY_ANOMALY_SETUP_DATA_CALL_FAILURE, null), 0, 12); mQnsFrequentApnTypeChangeAnomalyReportThreshold = parseSlidingWindowCounterThreshold( properties.getString(KEY_ANOMALY_QNS_CHANGE_NETWORK, null), 0, 5); mIsInvalidQnsParamAnomalyReportEnabled = properties.getBoolean( KEY_ANOMALY_QNS_PARAM, false); mNetworkConnectingTimeout = properties.getInt( KEY_ANOMALY_NETWORK_CONNECTING_TIMEOUT, DEFAULT_NETWORK_TRANSIT_STATE_TIMEOUT_MS); mNetworkDisconnectingTimeout = properties.getInt( Loading Loading @@ -829,6 +842,23 @@ public class DataConfigManager extends Handler { return mImsReleaseRequestAnomalyReportThreshold; } /** * Anomaly report thresholds for frequent QNS change of preferred network * at {@link AccessNetworksManager} * @return EventFrequency to trigger the anomaly report */ public @NonNull EventFrequency getAnomalyQnsChangeThreshold() { return mQnsFrequentApnTypeChangeAnomalyReportThreshold; } /** * @return {@code true} if enabled anomaly report for invalid param when QNS wants to change * preferred network at {@link AccessNetworksManager}. */ public boolean isInvalidQnsParamAnomalyReportEnabled() { return mIsInvalidQnsParamAnomalyReportEnabled; } /** * @return Timeout in ms before creating an anomaly report for a DataNetwork stuck in * {@link DataNetwork.ConnectingState}. Loading
tests/telephonytests/src/com/android/internal/telephony/data/AccessNetworksManagerTest.java +7 −0 Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ public class AccessNetworksManagerTest extends TelephonyTest { private IQualifiedNetworksService mMockedQns; private IBinder mMockedIBinder; private AccessNetworksManagerCallback mMockedCallback; private DataConfigManager mMockedDataConfigManager; // The real callback passed created by AccessNetworksManager. private IQualifiedNetworksServiceCallback.Stub mQnsCallback; Loading Loading @@ -106,8 +107,14 @@ public class AccessNetworksManagerTest extends TelephonyTest { return null; }).when(mMockedCallback).invokeFromExecutor(any(Runnable.class)); mMockedDataConfigManager = Mockito.mock(DataConfigManager.class); doReturn(new DataConfigManager.EventFrequency(0, 2)) .when(mMockedDataConfigManager).getAnomalyQnsChangeThreshold(); mAccessNetworksManager = new AccessNetworksManager(mPhone, Looper.myLooper()); processAllMessages(); replaceInstance(AccessNetworksManager.class, "mDataConfigManager", mAccessNetworksManager, mMockedDataConfigManager); assumeFalse(mAccessNetworksManager.isInLegacyMode()); logd("-setUp"); } Loading