diff options
author | xshu <xshu@google.com> | 2020-04-20 17:29:53 -0700 |
---|---|---|
committer | xshu <xshu@google.com> | 2020-04-30 17:50:00 -0700 |
commit | 7ab314bb0e004d138a11ef3e3e3dbfa7670cc4d8 (patch) | |
tree | 12f9ddaf2aa0c634a69920bc902faebe91f1af7d | |
parent | 11f1b3d49fc0c96e73eae0c993edda46228bba13 (diff) |
Metered stats metrics
Count the number of saved and ephemeral networks observed as metered vs unmetered.
Also, notice the metered status before network selection, when possible.
Bug: 142322164
Test: com.android.server.wifi
Change-Id: Ifeff1522720764d00967534317d431d40335fd5f
7 files changed, 175 insertions, 4 deletions
diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java index 7314bb853..00bae8a1e 100644 --- a/service/java/com/android/server/wifi/ClientModeImpl.java +++ b/service/java/com/android/server/wifi/ClientModeImpl.java @@ -3086,6 +3086,9 @@ public class ClientModeImpl extends StateMachine { // Set meteredHint if DHCP result says network is metered if (dhcpResults.vendorInfo != null && dhcpResults.vendorInfo.contains("ANDROID_METERED")) { mWifiInfo.setMeteredHint(true); + mWifiMetrics.addMeteredStat(config, true); + } else { + mWifiMetrics.addMeteredStat(config, false); } updateCapabilities(config); diff --git a/service/java/com/android/server/wifi/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java index 4c0627af6..ca2e90552 100644 --- a/service/java/com/android/server/wifi/WifiMetrics.java +++ b/service/java/com/android/server/wifi/WifiMetrics.java @@ -71,6 +71,7 @@ import com.android.server.wifi.proto.nano.WifiMetricsProto.LinkProbeStats; import com.android.server.wifi.proto.nano.WifiMetricsProto.LinkProbeStats.ExperimentProbeCounts; import com.android.server.wifi.proto.nano.WifiMetricsProto.LinkProbeStats.LinkProbeFailureReasonCount; import com.android.server.wifi.proto.nano.WifiMetricsProto.LinkSpeedCount; +import com.android.server.wifi.proto.nano.WifiMetricsProto.MeteredNetworkStats; import com.android.server.wifi.proto.nano.WifiMetricsProto.NetworkSelectionExperimentDecisions; import com.android.server.wifi.proto.nano.WifiMetricsProto.PasspointProfileTypeCount; import com.android.server.wifi.proto.nano.WifiMetricsProto.PasspointProvisionStats; @@ -351,6 +352,8 @@ public class WifiMetrics { private final IntHistogram mLinkProbeSuccessElapsedTimeMsHistogram = new IntHistogram( LINK_PROBE_ELAPSED_TIME_MS_HISTOGRAM_BUCKETS); private final IntCounter mLinkProbeFailureReasonCounts = new IntCounter(); + private final MeteredNetworkStatsBuilder mMeteredNetworkStatsBuilder = + new MeteredNetworkStatsBuilder(); /** * Maps a String link probe experiment ID to the number of link probes that were sent for this @@ -3077,7 +3080,10 @@ public class WifiMetrics { + mWifiLogProto.numExternalForegroundAppOneshotScanRequestsThrottled); pw.println("mWifiLogProto.numExternalBackgroundAppOneshotScanRequestsThrottled=" + mWifiLogProto.numExternalBackgroundAppOneshotScanRequestsThrottled); - + pw.println("mWifiLogProto.meteredNetworkStatsSaved="); + pw.println(mMeteredNetworkStatsBuilder.toProto(false)); + pw.println("mWifiLogProto.meteredNetworkStatsSuggestion="); + pw.println(mMeteredNetworkStatsBuilder.toProto(true)); pw.println("mScanReturnEntries:"); pw.println(" SCAN_UNKNOWN: " + getScanReturnEntry( WifiMetricsProto.WifiLog.SCAN_UNKNOWN)); @@ -3701,6 +3707,7 @@ public class WifiMetrics { mWifiLogProto.numNetworksAddedByApps = 0; mWifiLogProto.numHiddenNetworks = 0; mWifiLogProto.numPasspointNetworks = 0; + for (WifiConfiguration config : networks) { if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)) { mWifiLogProto.numOpenNetworks++; @@ -4262,6 +4269,8 @@ public class WifiMetrics { mRxThroughputMbpsHistogram2G.toProto(); mWifiLogProto.throughputMbpsHistogram.rxAbove2G = mRxThroughputMbpsHistogramAbove2G.toProto(); + mWifiLogProto.meteredNetworkStatsSaved = mMeteredNetworkStatsBuilder.toProto(false); + mWifiLogProto.meteredNetworkStatsSuggestion = mMeteredNetworkStatsBuilder.toProto(true); InitPartialScanStats initialPartialScanStats = new InitPartialScanStats(); initialPartialScanStats.numScans = mInitPartialScanTotalCount; @@ -4444,6 +4453,7 @@ public class WifiMetrics { mProbeElapsedTimeSinceLastUpdateMs = -1; mProbeMcsRateSinceLastUpdate = -1; mScoreBreachLowTimeMillis = -1; + mMeteredNetworkStatsBuilder.clear(); mWifiConfigStoreReadDurationHistogram.clear(); mWifiConfigStoreWriteDurationHistogram.clear(); mLinkProbeSuccessRssiCounts.clear(); @@ -5078,6 +5088,72 @@ public class WifiMetrics { } return result; } + + static class MeteredNetworkStatsBuilder { + // A map from network identifier to MeteredDetail + Map<String, MeteredDetail> mNetworkMap = new ArrayMap<>(); + + void put(WifiConfiguration config, boolean detectedAsMetered) { + MeteredDetail meteredDetail = new MeteredDetail(); + boolean isMetered = detectedAsMetered; + if (config.meteredOverride == WifiConfiguration.METERED_OVERRIDE_METERED) { + isMetered = true; + } else if (config.meteredOverride == WifiConfiguration.METERED_OVERRIDE_NOT_METERED) { + isMetered = false; + } + meteredDetail.isMetered = isMetered; + meteredDetail.isMeteredOverrideSet = config.meteredOverride + != WifiConfiguration.METERED_OVERRIDE_NONE; + meteredDetail.isFromSuggestion = config.fromWifiNetworkSuggestion; + mNetworkMap.put(config.getKey(), meteredDetail); + } + + void clear() { + mNetworkMap.clear(); + } + + MeteredNetworkStats toProto(boolean isFromSuggestion) { + MeteredNetworkStats result = new MeteredNetworkStats(); + for (MeteredDetail meteredDetail : mNetworkMap.values()) { + if (meteredDetail.isFromSuggestion != isFromSuggestion) { + continue; + } + if (meteredDetail.isMetered) { + result.numMetered++; + } else { + result.numUnmetered++; + } + if (meteredDetail.isMeteredOverrideSet) { + if (meteredDetail.isMetered) { + result.numOverrideMetered++; + } else { + result.numOverrideUnmetered++; + } + } + } + return result; + } + + static class MeteredDetail { + public boolean isMetered; + public boolean isMeteredOverrideSet; + public boolean isFromSuggestion; + } + } + + /** + * Add metered information of this network. + * @param config WifiConfiguration representing the netework. + * @param detectedAsMetered is the network detected as metered. + */ + public void addMeteredStat(WifiConfiguration config, boolean detectedAsMetered) { + synchronized (mLock) { + if (config == null) { + return; + } + mMeteredNetworkStatsBuilder.put(config, detectedAsMetered); + } + } /** * Logs a UserActionEvent without a target network. * @param eventType the type of user action (one of WifiMetricsProto.UserActionEvent.EventType) diff --git a/service/java/com/android/server/wifi/WifiNetworkSelector.java b/service/java/com/android/server/wifi/WifiNetworkSelector.java index da583df65..7e95fba0b 100644 --- a/service/java/com/android/server/wifi/WifiNetworkSelector.java +++ b/service/java/com/android/server/wifi/WifiNetworkSelector.java @@ -676,16 +676,24 @@ public class WifiNetworkSelector { * This is sticky to prevent continuous flip-flopping between networks, when the metered * status is learned after association. */ - private boolean isEverMetered(@NonNull WifiConfiguration config, @Nullable WifiInfo info) { + private boolean isEverMetered(@NonNull WifiConfiguration config, @Nullable WifiInfo info, + @NonNull ScanDetail scanDetail) { // If info does not match config, don't use it. - // TODO(b/149988649) Metrics if (info != null && info.getNetworkId() != config.networkId) info = null; boolean metered = WifiConfiguration.isMetered(config, info); + NetworkDetail networkDetail = scanDetail.getNetworkDetail(); + if (networkDetail != null + && networkDetail.getAnt() + == NetworkDetail.Ant.ChargeablePublic) { + metered = true; + } + mWifiMetrics.addMeteredStat(config, metered); if (config.meteredOverride != WifiConfiguration.METERED_OVERRIDE_NONE) { // User override is in effect; we should trust it if (mKnownMeteredNetworkIds.remove(config.networkId)) { localLog("KnownMeteredNetworkIds = " + mKnownMeteredNetworkIds); } + metered = config.meteredOverride == WifiConfiguration.METERED_OVERRIDE_METERED; } else if (mKnownMeteredNetworkIds.contains(config.networkId)) { // Use the saved information metered = true; @@ -794,7 +802,7 @@ public class WifiNetworkSelector { WifiCandidates.Key key = wifiCandidates.keyFromScanDetailAndConfig( scanDetail, config); if (key != null) { - boolean metered = isEverMetered(config, wifiInfo); + boolean metered = isEverMetered(config, wifiInfo, scanDetail); // TODO(b/151981920) Saved passpoint candidates are marked ephemeral boolean added = wifiCandidates.add(key, config, registeredNominator.getId(), diff --git a/service/proto/src/metrics.proto b/service/proto/src/metrics.proto index d2e769d8c..c57101f06 100644 --- a/service/proto/src/metrics.proto +++ b/service/proto/src/metrics.proto @@ -694,6 +694,12 @@ message WifiLog { // Does the user have enhanced MAC randomization forced to on. optional bool is_enhanced_mac_randomization_force_enabled = 194; + + // Metered stats for saved networks. + optional MeteredNetworkStats metered_network_stats_saved = 195; + + // Metered stats for suggestion networks. + optional MeteredNetworkStats metered_network_stats_suggestion = 196; } // Information that gets logged for every WiFi connection. @@ -3055,6 +3061,21 @@ message PasspointProvisionStats { } } +// Counts number of networks on the device that are metered. +message MeteredNetworkStats { + // Number of networks that are set or detected as metered. + optional int32 num_metered = 1 [default = 0]; + + // Number of networks that are set or detected as unmetered. + optional int32 num_unmetered = 2; + + // Number of networks that have meteredOverride set to metered. + optional int32 num_override_metered = 3; + + // Number of networks that have meteredOverride set to unmetered. + optional int32 num_override_unmetered = 4; +} + // An event capturing user action on wifi message UserActionEvent { enum EventType { diff --git a/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java b/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java index 8f36770e6..77f9bf910 100644 --- a/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java +++ b/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java @@ -1028,6 +1028,8 @@ public class ClientModeImplTest extends WifiBaseTest { injectDhcpSuccess(dhcpResults); mLooper.dispatchAll(); + // Verify WifiMetrics logging for metered metrics based on DHCP results + verify(mWifiMetrics).addMeteredStat(any(), anyBoolean()); WifiInfo wifiInfo = mCmi.getWifiInfo(); assertNotNull(wifiInfo); assertEquals(sBSSID, wifiInfo.getBSSID()); diff --git a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java index d374dc9ce..d2c292221 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java @@ -4468,6 +4468,66 @@ public class WifiMetricsTest extends WifiBaseTest { assertEquals(4, mDecodedProto.wifiToggleStats.numToggleOffNormal); } + /** + * Verify metered stats are counted properly for saved and ephemeral networks. + */ + @Test + public void testMeteredNetworkMetrics() throws Exception { + // Test without metered override + WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork(); + WifiConfiguration config1 = WifiConfigurationTestUtil.createPskNetwork(); + config.fromWifiNetworkSuggestion = false; + config1.fromWifiNetworkSuggestion = true; + mWifiMetrics.addMeteredStat(config, false); + mWifiMetrics.addMeteredStat(config1, true); + dumpProtoAndDeserialize(); + assertEquals(0, mDecodedProto.meteredNetworkStatsSaved.numMetered); + assertEquals(1, mDecodedProto.meteredNetworkStatsSaved.numUnmetered); + assertEquals(0, mDecodedProto.meteredNetworkStatsSaved.numOverrideMetered); + assertEquals(0, mDecodedProto.meteredNetworkStatsSaved.numOverrideUnmetered); + assertEquals(1, mDecodedProto.meteredNetworkStatsSuggestion.numMetered); + assertEquals(0, mDecodedProto.meteredNetworkStatsSuggestion.numUnmetered); + assertEquals(0, mDecodedProto.meteredNetworkStatsSuggestion.numOverrideMetered); + assertEquals(0, mDecodedProto.meteredNetworkStatsSuggestion.numOverrideUnmetered); + + // Test with metered override + config = WifiConfigurationTestUtil.createPskNetwork(); + config1 = WifiConfigurationTestUtil.createPskNetwork(); + config.meteredOverride = WifiConfiguration.METERED_OVERRIDE_METERED; + config1.meteredOverride = WifiConfiguration.METERED_OVERRIDE_NOT_METERED; + mWifiMetrics.addMeteredStat(config, true); + mWifiMetrics.addMeteredStat(config1, true); + dumpProtoAndDeserialize(); + assertEquals(1, mDecodedProto.meteredNetworkStatsSaved.numMetered); + assertEquals(1, mDecodedProto.meteredNetworkStatsSaved.numUnmetered); + assertEquals(1, mDecodedProto.meteredNetworkStatsSaved.numOverrideMetered); + assertEquals(1, mDecodedProto.meteredNetworkStatsSaved.numOverrideUnmetered); + assertEquals(0, mDecodedProto.meteredNetworkStatsSuggestion.numMetered); + assertEquals(0, mDecodedProto.meteredNetworkStatsSuggestion.numUnmetered); + assertEquals(0, mDecodedProto.meteredNetworkStatsSuggestion.numOverrideMetered); + assertEquals(0, mDecodedProto.meteredNetworkStatsSuggestion.numOverrideUnmetered); + } + + /** + * Verify that the same network does not get counted twice + */ + @Test + public void testMeteredNetworkMetricsNoDoubleCount() throws Exception { + WifiConfiguration config = new WifiConfiguration(); + config.ephemeral = false; + mWifiMetrics.addMeteredStat(config, false); + mWifiMetrics.addMeteredStat(config, true); + mWifiMetrics.addMeteredStat(config, true); + dumpProtoAndDeserialize(); + assertEquals(1, mDecodedProto.meteredNetworkStatsSaved.numMetered); + assertEquals(0, mDecodedProto.meteredNetworkStatsSaved.numUnmetered); + assertEquals(0, mDecodedProto.meteredNetworkStatsSaved.numOverrideMetered); + assertEquals(0, mDecodedProto.meteredNetworkStatsSaved.numOverrideUnmetered); + assertEquals(0, mDecodedProto.meteredNetworkStatsSuggestion.numMetered); + assertEquals(0, mDecodedProto.meteredNetworkStatsSuggestion.numUnmetered); + assertEquals(0, mDecodedProto.meteredNetworkStatsSuggestion.numOverrideMetered); + assertEquals(0, mDecodedProto.meteredNetworkStatsSuggestion.numOverrideUnmetered); + } /** * Create a test to verify data collection logic triggered by score breaching low diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java index 7c5ff1f3f..f8c47a2f6 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java @@ -596,6 +596,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { verify(mWifiConfigManager, times(savedConfigs.length)).tryEnableNetwork(anyInt()); verify(mWifiConfigManager, times(savedConfigs.length)) .clearNetworkCandidateScanResult(anyInt()); + verify(mWifiMetrics, atLeastOnce()).addMeteredStat(any(), anyBoolean()); } /** |