diff options
author | Michael Plass <mplass@google.com> | 2019-10-30 16:19:16 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2019-10-30 16:19:16 +0000 |
commit | 39fd9697d1b3070730b160b35a346609fd8f2ff8 (patch) | |
tree | 8a85d453c85fc950245ad2fd0e71a1e0d31b1601 | |
parent | 2124587f8af4ca1f9f2787747dd65d7eb9db9708 (diff) | |
parent | df76448a89e7ccc2e82a682ff0bea0d468f35be6 (diff) |
Merge "[WifiScoreCard] Add histograms"
-rw-r--r-- | service/java/com/android/server/wifi/WifiScoreCard.java | 82 | ||||
-rw-r--r-- | service/proto/src/scorecard.proto | 12 | ||||
-rw-r--r-- | tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java | 36 |
3 files changed, 96 insertions, 34 deletions
diff --git a/service/java/com/android/server/wifi/WifiScoreCard.java b/service/java/com/android/server/wifi/WifiScoreCard.java index af995fd62..77abdb1e9 100644 --- a/service/java/com/android/server/wifi/WifiScoreCard.java +++ b/service/java/com/android/server/wifi/WifiScoreCard.java @@ -33,11 +33,13 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.server.wifi.WifiScoreCardProto.AccessPoint; import com.android.server.wifi.WifiScoreCardProto.Event; +import com.android.server.wifi.WifiScoreCardProto.HistogramBucket; import com.android.server.wifi.WifiScoreCardProto.Network; import com.android.server.wifi.WifiScoreCardProto.NetworkList; import com.android.server.wifi.WifiScoreCardProto.SecurityType; import com.android.server.wifi.WifiScoreCardProto.Signal; import com.android.server.wifi.WifiScoreCardProto.UnivariateStatistic; +import com.android.server.wifi.util.IntHistogram; import com.android.server.wifi.util.NativeUtil; import com.google.protobuf.ByteString; @@ -68,12 +70,21 @@ public class WifiScoreCard { private static final String TAG = "WifiScoreCard"; private static final boolean DBG = false; + @VisibleForTesting + boolean mPersistentHistograms = false; // not ready yet + private static final int TARGET_IN_MEMORY_ENTRIES = 50; private final Clock mClock; private final String mL2KeySeed; private MemoryStore mMemoryStore; + @VisibleForTesting + static final int[] RSSI_BUCKETS = + {-99, -88, -87, -86, -85, -84, -83, -82, -81, -80, -79, -78, -77, -76, -75, -74, -73, + -72, -71, -70, -66, -55}; + + /** Our view of the memory store */ public interface MemoryStore { /** Requests a read, with asynchronous reply */ @@ -393,17 +404,19 @@ public class WifiScoreCard { PerSignal perSignal = lookupSignal(event, frequency); if (rssi != INVALID_RSSI) { perSignal.rssi.update(rssi); + changed = true; } if (linkspeed > 0) { perSignal.linkspeed.update(linkspeed); + changed = true; } if (perSignal.elapsedMs != null && mTsConnectionAttemptStart > TS_NONE) { long millis = mClock.getElapsedSinceBootMillis() - mTsConnectionAttemptStart; if (millis >= 0) { perSignal.elapsedMs.update(millis); + changed = true; } } - changed = true; } PerSignal lookupSignal(Event event, int frequency) { finishPendingRead(); @@ -470,7 +483,8 @@ public class WifiScoreCard { Pair<Event, Integer> key = new Pair<>(signal.getEvent(), signal.getFrequency()); PerSignal perSignal = mSignalForEventAndFrequency.get(key); if (perSignal == null) { - mSignalForEventAndFrequency.put(key, new PerSignal(signal)); + mSignalForEventAndFrequency.put(key, + new PerSignal(key.first, key.second).merge(signal)); // No need to set changed for this, since we are in sync with what's stored } else { perSignal.merge(signal); @@ -690,7 +704,8 @@ public class WifiScoreCard { PerSignal(Event event, int frequency) { this.event = event; this.frequency = frequency; - this.rssi = new PerUnivariateStatistic(); + // TODO(b/136675430) - histograms not needed for all events? + this.rssi = new PerUnivariateStatistic(RSSI_BUCKETS); this.linkspeed = new PerUnivariateStatistic(); switch (event) { case FIRST_POLL_AFTER_CONNECTION: @@ -706,18 +721,7 @@ public class WifiScoreCard { break; } } - PerSignal(Signal signal) { - this.event = signal.getEvent(); - this.frequency = signal.getFrequency(); - this.rssi = new PerUnivariateStatistic(signal.getRssi()); - this.linkspeed = new PerUnivariateStatistic(signal.getLinkspeed()); - if (signal.hasElapsedMs()) { - this.elapsedMs = new PerUnivariateStatistic(signal.getElapsedMs()); - } else { - this.elapsedMs = null; - } - } - void merge(Signal signal) { + PerSignal merge(Signal signal) { Preconditions.checkArgument(event == signal.getEvent()); Preconditions.checkArgument(frequency == signal.getFrequency()); rssi.merge(signal.getRssi()); @@ -725,6 +729,7 @@ public class WifiScoreCard { if (signal.hasElapsedMs()) { elapsedMs.merge(signal.getElapsedMs()); } + return this; } Signal toSignal() { Signal.Builder builder = Signal.newBuilder(); @@ -735,6 +740,9 @@ public class WifiScoreCard { if (elapsedMs != null) { builder.setElapsedMs(elapsedMs.toUnivariateStatistic()); } + if (DBG && rssi.intHistogram != null && rssi.intHistogram.numNonEmptyBuckets() > 0) { + Log.d(TAG, "Histogram " + event + " RSSI" + rssi.intHistogram); + } return builder.build(); } } @@ -747,25 +755,10 @@ public class WifiScoreCard { public double maxValue = Double.NEGATIVE_INFINITY; public double historicalMean = 0.0; public double historicalVariance = Double.POSITIVE_INFINITY; + public IntHistogram intHistogram = null; PerUnivariateStatistic() {} - PerUnivariateStatistic(UnivariateStatistic stats) { - if (stats.hasCount()) { - this.count = stats.getCount(); - this.sum = stats.getSum(); - this.sumOfSquares = stats.getSumOfSquares(); - } - if (stats.hasMinValue()) { - this.minValue = stats.getMinValue(); - } - if (stats.hasMaxValue()) { - this.maxValue = stats.getMaxValue(); - } - if (stats.hasHistoricalMean()) { - this.historicalMean = stats.getHistoricalMean(); - } - if (stats.hasHistoricalVariance()) { - this.historicalVariance = stats.getHistoricalVariance(); - } + PerUnivariateStatistic(int[] bucketBoundaries) { + intHistogram = new IntHistogram(bucketBoundaries); } void update(double value) { count++; @@ -773,6 +766,9 @@ public class WifiScoreCard { sumOfSquares += value * value; minValue = Math.min(minValue, value); maxValue = Math.max(maxValue, value); + if (intHistogram != null) { + intHistogram.add(Math.round((float) value), 1); + } } void age() { //TODO Fold the current stats into the historical stats @@ -806,6 +802,18 @@ public class WifiScoreCard { historicalVariance = stats.getHistoricalVariance(); } } + if (intHistogram != null) { + for (HistogramBucket bucket : stats.getBucketsList()) { + long low = bucket.getLow(); + long count = bucket.getNumber(); + if (low != (int) low || count != (int) count || count < 0) { + Log.e(TAG, "Found corrupted histogram! Clearing."); + intHistogram.clear(); + break; + } + intHistogram.add((int) low, (int) count); + } + } } UnivariateStatistic toUnivariateStatistic() { UnivariateStatistic.Builder builder = UnivariateStatistic.newBuilder(); @@ -820,6 +828,14 @@ public class WifiScoreCard { builder.setHistoricalMean(historicalMean) .setHistoricalVariance(historicalVariance); } + if (mPersistentHistograms + && intHistogram != null && intHistogram.numNonEmptyBuckets() > 0) { + for (IntHistogram.Bucket b : intHistogram) { + if (b.count == 0) continue; + builder.addBuckets( + HistogramBucket.newBuilder().setLow(b.start).setNumber(b.count)); + } + } return builder.build(); } } diff --git a/service/proto/src/scorecard.proto b/service/proto/src/scorecard.proto index 27e1e13cb..28a489887 100644 --- a/service/proto/src/scorecard.proto +++ b/service/proto/src/scorecard.proto @@ -94,8 +94,20 @@ message UnivariateStatistic { // more recent measurements get a higher weight. optional double historical_mean = 6; // Long-term average optional double historical_variance = 7; // Long-term variance + + // Arranged by increasing value + repeated HistogramBucket buckets = 8; }; +message HistogramBucket { + // Lower bound (inclusive) for values falling in this bucket. + // Compact signed encoding used here, because rssi values are negative. + // The upper bound is not stored explicitly. + optional sint64 low = 1; + // Number of occurences for this value or bucket. + optional int64 number = 2; +} + // Events where statistics may be collected enum Event { SIGNAL_POLL = 1; diff --git a/tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java b/tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java index e649b2808..b78b248fa 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java @@ -34,6 +34,7 @@ import com.android.server.wifi.WifiScoreCardProto.Event; import com.android.server.wifi.WifiScoreCardProto.Network; import com.android.server.wifi.WifiScoreCardProto.NetworkList; import com.android.server.wifi.WifiScoreCardProto.Signal; +import com.android.server.wifi.util.IntHistogram; import org.junit.Before; import org.junit.Test; @@ -98,6 +99,7 @@ public class WifiScoreCardTest extends WifiBaseTest { mWifiInfo.setNetworkId(TEST_NETWORK_CONFIG_ID); millisecondsPass(0); mWifiScoreCard = new WifiScoreCard(mClock, "some seed"); + mWifiScoreCard.mPersistentHistograms = true; // TODO - remove when ready } /** @@ -256,6 +258,15 @@ public class WifiScoreCardTest extends WifiBaseTest { millisecondsPass(1000); mWifiInfo.setRssi(-44); mWifiScoreCard.noteSignalPoll(mWifiInfo); + mWifiInfo.setFrequency(2432); + for (int round = 0; round < 4; round++) { + for (int i = 0; i < HISTOGRAM_COUNT.length; i++) { + if (HISTOGRAM_COUNT[i] > round) { + mWifiInfo.setRssi(HISTOGRAM_RSSI[i]); + mWifiScoreCard.noteSignalPoll(mWifiInfo); + } + } + } WifiScoreCard.PerBssid perBssid = mWifiScoreCard.fetchByBssid(TEST_BSSID_1); perBssid.lookupSignal(Event.SIGNAL_POLL, 2412).rssi.historicalMean = -42.0; perBssid.lookupSignal(Event.SIGNAL_POLL, 2412).rssi.historicalVariance = 4.0; @@ -264,6 +275,21 @@ public class WifiScoreCardTest extends WifiBaseTest { byte[] serialized = perBssid.toAccessPoint().toByteArray(); return serialized; } + private static final int[] HISTOGRAM_RSSI = {-80, -79, -78}; + private static final int[] HISTOGRAM_COUNT = {3, 1, 4}; + + private void checkHistogramExample(String diag, IntHistogram rssiHistogram) { + int i = 0; + for (IntHistogram.Bucket bucket : rssiHistogram) { + if (bucket.count != 0) { + assertTrue(diag, i < HISTOGRAM_COUNT.length); + assertEquals(diag, HISTOGRAM_RSSI[i], bucket.start); + assertEquals(diag, HISTOGRAM_COUNT[i], bucket.count); + i++; + } + } + assertEquals(diag, HISTOGRAM_COUNT.length, i); + } /** * Checks that the fields of the serialization example are as expected @@ -283,6 +309,8 @@ public class WifiScoreCardTest extends WifiBaseTest { .rssi.historicalMean, TOL); assertEquals(diag, 4.0, perBssid.lookupSignal(Event.SIGNAL_POLL, 2412) .rssi.historicalVariance, TOL); + checkHistogramExample(diag, perBssid.lookupSignal(Event.SIGNAL_POLL, + 2432).rssi.intHistogram); } /** @@ -294,7 +322,7 @@ public class WifiScoreCardTest extends WifiBaseTest { // Verify by parsing it and checking that we see the expected results AccessPoint ap = AccessPoint.parseFrom(serialized); - assertEquals(4, ap.getEventStatsCount()); + assertEquals(5, ap.getEventStatsCount()); for (Signal signal: ap.getEventStatsList()) { if (signal.getFrequency() == 2412) { assertFalse(signal.getRssi().hasCount()); @@ -302,6 +330,12 @@ public class WifiScoreCardTest extends WifiBaseTest { assertEquals(4.0, signal.getRssi().getHistoricalVariance(), TOL); continue; } + if (signal.getFrequency() == 2432) { + assertEquals(Event.SIGNAL_POLL, signal.getEvent()); + assertEquals(HISTOGRAM_RSSI[2], signal.getRssi().getBuckets(2).getLow()); + assertEquals(HISTOGRAM_COUNT[2], signal.getRssi().getBuckets(2).getNumber()); + continue; + } assertEquals(5805, signal.getFrequency()); switch (signal.getEvent()) { case IP_CONFIGURATION_SUCCESS: |