summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Plass <mplass@google.com>2019-10-30 16:19:16 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2019-10-30 16:19:16 +0000
commit39fd9697d1b3070730b160b35a346609fd8f2ff8 (patch)
tree8a85d453c85fc950245ad2fd0e71a1e0d31b1601
parent2124587f8af4ca1f9f2787747dd65d7eb9db9708 (diff)
parentdf76448a89e7ccc2e82a682ff0bea0d468f35be6 (diff)
Merge "[WifiScoreCard] Add histograms"
-rw-r--r--service/java/com/android/server/wifi/WifiScoreCard.java82
-rw-r--r--service/proto/src/scorecard.proto12
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java36
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: