summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Peng Wang <wanpeng@google.com> 2024-11-11 10:23:48 -0800
committer Peng Wang <wanpeng@google.com> 2024-12-11 23:07:46 +0000
commit7584b9646be1359b28a0981dc87c8b28627358ff (patch)
treeea97dde8c2dc29af6afeb242b8454e4983ce8c8d
parent3b1b5900b0506850f271e67b03dec49f41519c6b (diff)
Add WiFi Scorer data capture function in framework
Summary of new data capture: 1. Add timestamp_offset field, to record the offset between entry timstamp and capture period start time 2. Add store_time_offset, to record the offset between capture period stop time and captured data stored time 3. Add mWifiUsabilityStatsTrainingList to store all upload candidates 4. Floor capture period start time, to nearest hour 5. Extract WifiUsabilityStatsEntry, whose timestamps are within capture period. Set the timeStampMs to 0 due to privacy request; update the timestampOffset field Flag: EXEMPT, API call will be gated in WiFi Scorer App Bug: 377723852 Test: atest, on device test Change-Id: Ia895ca9571e0fe09618d2cf9b6eda243a7853786
-rw-r--r--service/java/com/android/server/wifi/Clock.java6
-rw-r--r--service/java/com/android/server/wifi/WifiMetrics.java96
-rw-r--r--service/proto/src/metrics.proto17
-rw-r--r--service/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java196
4 files changed, 308 insertions, 7 deletions
diff --git a/service/java/com/android/server/wifi/Clock.java b/service/java/com/android/server/wifi/Clock.java
index 63e958b3ca..1a235287cb 100644
--- a/service/java/com/android/server/wifi/Clock.java
+++ b/service/java/com/android/server/wifi/Clock.java
@@ -18,6 +18,8 @@ package com.android.server.wifi;
import android.os.SystemClock;
+import java.time.Instant;
+
import javax.annotation.concurrent.ThreadSafe;
/**
@@ -68,4 +70,8 @@ public class Clock {
public void sleep(long ms) {
SystemClock.sleep(ms);
}
+
+ public Instant getCurrentInstant() {
+ return Instant.now();
+ }
}
diff --git a/service/java/com/android/server/wifi/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java
index 1f9d54a927..8be74e57de 100644
--- a/service/java/com/android/server/wifi/WifiMetrics.java
+++ b/service/java/com/android/server/wifi/WifiMetrics.java
@@ -144,6 +144,7 @@ import com.android.server.wifi.proto.nano.WifiMetricsProto.SoftApConnectedClient
import com.android.server.wifi.proto.nano.WifiMetricsProto.StaEvent;
import com.android.server.wifi.proto.nano.WifiMetricsProto.StaEvent.ConfigInfo;
import com.android.server.wifi.proto.nano.WifiMetricsProto.TargetNetworkInfo;
+import com.android.server.wifi.proto.nano.WifiMetricsProto.TrainingData;
import com.android.server.wifi.proto.nano.WifiMetricsProto.UserActionEvent;
import com.android.server.wifi.proto.nano.WifiMetricsProto.UserReactionToApprovalUiEvent;
import com.android.server.wifi.proto.nano.WifiMetricsProto.UserReactionToApprovalUiEvent.UserReaction;
@@ -158,6 +159,7 @@ import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiToWifiSwitchStats
import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiToggleStats;
import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiUsabilityStats; // This contains a time series of WifiUsabilityStatsEntry along with some metadata, such as the label of the time series or trigger type.
import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiUsabilityStatsEntry; // This contains all the stats for a single point in time.
+import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiUsabilityStatsTraining;
import com.android.server.wifi.rtt.RttMetrics;
import com.android.server.wifi.scanner.KnownBandsChannelHelper;
import com.android.server.wifi.util.InformationElementUtil;
@@ -178,6 +180,8 @@ import org.json.JSONObject;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.time.Duration;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
@@ -244,6 +248,7 @@ public class WifiMetrics {
public static final int MIN_DATA_STALL_WAIT_MS = 120 * 1000; // 2 minutes
// Max number of WifiUsabilityStatsEntry elements to store in the ringbuffer.
public static final int MAX_WIFI_USABILITY_STATS_ENTRIES_RING_BUFFER_SIZE = 80;
+ public static final int MAX_WIFI_USABILITY_STATS_TRAINING_SIZE = 10;
// Max number of WifiUsabilityStats records to store for each type.
public static final int MAX_WIFI_USABILITY_STATS_RECORDS_PER_TYPE = 10;
// Max number of WifiUsabilityStats per labeled type to upload to server
@@ -280,6 +285,8 @@ public class WifiMetrics {
public static final int MIN_DOWNSTREAM_BANDWIDTH_KBPS = 1000;
public static final int MIN_UPSTREAM_BANDWIDTH_KBPS = 1000;
public static final int INVALID_SPEED = -1;
+ public static final long MILLIS_IN_A_SECOND = 1000;
+ public static final long MILLIS_IN_AN_HOUR = 3600 * 1000;
private Clock mClock;
private boolean mScreenOn;
@@ -500,6 +507,12 @@ public class WifiMetrics {
@VisibleForTesting
public final LinkedList<WifiUsabilityStatsEntry> mWifiUsabilityStatsEntriesRingBuffer =
new LinkedList<>();
+ // Each WifiUsabilityStatsTraining instance contains a list of WifiUsabilityStatsEntry objects,
+ // representing a time series of WiFi usability statistics recorded within a specific data
+ // capture period. It also includes information about the type of data capture and the duration
+ // of the capture period.
+ public final List<WifiUsabilityStatsTraining> mWifiUsabilityStatsTrainingExamples =
+ new ArrayList<>();
// One WifiUsabilityStats contains a single time series of WifiUsabilityStatsEntry along with
// some metadata. These LinkedList's below contain sets of time series that are labeled as
// either 'good' or 'bad'.
@@ -5001,6 +5014,17 @@ public class WifiMetrics {
printWifiUsabilityStatsEntry(pw, stats);
}
+ pw.println("mWifiUsabilityStatsTrainingExamples:");
+ for (WifiUsabilityStatsTraining statsTraining
+ : mWifiUsabilityStatsTrainingExamples) {
+ pw.println("\ndata_capture_type=" + statsTraining.dataCaptureType);
+ pw.println("\ncapture_start_timestamp_secs="
+ + statsTraining.captureStartTimestampSecs);
+ for (WifiUsabilityStatsEntry stats : statsTraining.trainingData.stats) {
+ printWifiUsabilityStatsEntry(pw, stats);
+ }
+ }
+
pw.println("mMobilityStatePnoStatsMap:");
for (int i = 0; i < mMobilityStatePnoStatsMap.size(); i++) {
printDeviceMobilityStatePnoScanStats(pw, mMobilityStatePnoStatsMap.valueAt(i));
@@ -5768,6 +5792,12 @@ public class WifiMetrics {
}
mWifiLogProto.hardwareRevision = SystemProperties.get("ro.boot.revision", "");
+ mWifiLogProto.wifiUsabilityStatsTraining =
+ new WifiUsabilityStatsTraining[mWifiUsabilityStatsTrainingExamples.size()];
+ for (int i = 0; i < mWifiUsabilityStatsTrainingExamples.size(); i++) {
+ mWifiLogProto.wifiUsabilityStatsTraining[i] =
+ mWifiUsabilityStatsTrainingExamples.remove(0);
+ }
mWifiLogProto.mobilityStatePnoStatsList =
new DeviceMobilityStatePnoScanStats[mMobilityStatePnoStatsMap.size()];
for (int i = 0; i < mMobilityStatePnoStatsMap.size(); i++) {
@@ -7163,10 +7193,62 @@ public class WifiMetrics {
*/
public int storeCapturedData(int triggerType, boolean isFullCapture,
long triggerStartTimeMillis, long triggerStopTimeMillis) {
- // TODO: Implement how to extract WifiUsabilityStatsEntries from ring buffer whose
- // timestamps are within [triggerStartTimeMillis, triggerStopTimeMillis]
Log.d(TAG, "storeCapturedData: triggerType=" + triggerType
+ ", isFullCapture=" + isFullCapture);
+
+ // Validate triggerStartTimeMillis and triggerStopTimeMillis in non full-capture case
+ if (!isFullCapture && ((triggerStartTimeMillis < 0 || triggerStopTimeMillis < 0
+ || triggerStopTimeMillis <= triggerStartTimeMillis))) {
+ return 1;
+ }
+
+ Instant now = mClock.getCurrentInstant();
+ Duration durationSinceBoot = Duration.ofMillis(mClock.getElapsedSinceBootMillis());
+
+ WifiUsabilityStatsTraining wifiUsabilityStatsTraining = new WifiUsabilityStatsTraining();
+ while (mWifiUsabilityStatsTrainingExamples.size()
+ >= MAX_WIFI_USABILITY_STATS_TRAINING_SIZE) {
+ mWifiUsabilityStatsTrainingExamples.remove(0);
+ }
+ wifiUsabilityStatsTraining.dataCaptureType = triggerType;
+
+ long capturePeriodStartTime = triggerStartTimeMillis;
+ long capturePeriodStopTime = triggerStopTimeMillis;
+
+ if (isFullCapture) {
+ capturePeriodStartTime = mWifiUsabilityStatsEntriesRingBuffer.size() > 0
+ ? mWifiUsabilityStatsEntriesRingBuffer.get(0).timeStampMs :
+ 0;
+ capturePeriodStopTime = mWifiUsabilityStatsEntriesRingBuffer.size() > 0
+ ? mWifiUsabilityStatsEntriesRingBuffer.get(
+ mWifiUsabilityStatsEntriesRingBuffer.size() - 1).timeStampMs :
+ durationSinceBoot.toMillis();
+ }
+
+ wifiUsabilityStatsTraining.captureStartTimestampSecs =
+ now.minus(durationSinceBoot)
+ .plus(Duration.ofMillis(capturePeriodStartTime))
+ .truncatedTo(ChronoUnit.HOURS)
+ .getEpochSecond();
+ wifiUsabilityStatsTraining.storeTimeOffsetMs =
+ durationSinceBoot.toMillis() - capturePeriodStopTime;
+
+ // If isFullCapture is true, store everything in ring buffer
+ // If isFullCapture is false, Store WifiUsabilityStatsEntries within capture period
+ TrainingData trainingData = new TrainingData();
+ List<WifiUsabilityStatsEntry> trainingDataList = new ArrayList<>();
+ for (WifiUsabilityStatsEntry currStats : mWifiUsabilityStatsEntriesRingBuffer) {
+ if (isFullCapture || (currStats.timeStampMs >= triggerStartTimeMillis
+ && currStats.timeStampMs < triggerStopTimeMillis)) {
+ WifiUsabilityStatsEntry trainingStats =
+ createNewWifiUsabilityStatsEntry(currStats, capturePeriodStartTime);
+ trainingDataList.add(trainingStats);
+ }
+ }
+ trainingData.stats = trainingDataList.toArray(new WifiUsabilityStatsEntry[0]);
+ wifiUsabilityStatsTraining.trainingData = trainingData;
+
+ mWifiUsabilityStatsTrainingExamples.add(wifiUsabilityStatsTraining);
return 0;
}
@@ -8112,9 +8194,11 @@ public class WifiMetrics {
}
}
- private WifiUsabilityStatsEntry createNewWifiUsabilityStatsEntry(WifiUsabilityStatsEntry s) {
+ private WifiUsabilityStatsEntry createNewWifiUsabilityStatsEntry(WifiUsabilityStatsEntry s,
+ long referenceTimestampMs) {
WifiUsabilityStatsEntry out = new WifiUsabilityStatsEntry();
- out.timeStampMs = s.timeStampMs;
+ // Privacy review suggests not to upload real timestamp
+ out.timeStampMs = 0;
out.totalTxSuccess = s.totalTxSuccess;
out.totalTxRetries = s.totalTxRetries;
out.totalTxBad = s.totalTxBad;
@@ -8173,6 +8257,7 @@ public class WifiMetrics {
out.voipMode = s.voipMode;
out.threadDeviceRole = s.threadDeviceRole;
out.statusDataStall = s.statusDataStall;
+ out.timestampOffsetMs = s.timeStampMs - referenceTimestampMs;
return out;
}
@@ -8213,7 +8298,8 @@ public class WifiMetrics {
new WifiUsabilityStatsEntry[mWifiUsabilityStatsEntriesRingBuffer.size()];
for (int i = 0; i < mWifiUsabilityStatsEntriesRingBuffer.size(); i++) {
wifiUsabilityStats.stats[i] =
- createNewWifiUsabilityStatsEntry(mWifiUsabilityStatsEntriesRingBuffer.get(i));
+ createNewWifiUsabilityStatsEntry(mWifiUsabilityStatsEntriesRingBuffer.get(i),
+ 0);
}
return wifiUsabilityStats;
}
diff --git a/service/proto/src/metrics.proto b/service/proto/src/metrics.proto
index 4a53854d81..979f85c981 100644
--- a/service/proto/src/metrics.proto
+++ b/service/proto/src/metrics.proto
@@ -2535,6 +2535,8 @@ message WifiLinkLayerUsageStats {
repeated RadioStats radio_stats = 11;
}
+// WifiUsabilityStatsEntry will only be uploaded when its timestamp is within the data capture
+// period
message WifiUsabilityStatsEntry {
// Status codes for link probe status
enum LinkProbeStatus {
@@ -2815,6 +2817,14 @@ message WifiUsabilityStatsEntry {
optional int32 capture_event_type_subcode = 64;
optional int32 status_data_stall = 65;
+
+ // If the full data capture is being stored (field isFullCapture is true in the function call
+ // WifiManager.storeCapturedData), this field will represent the time offset between this sample
+ // and the first sample in the capture buffer. i.e. this field will have the value 0 for the
+ // first sample in the capture buffer.
+ // If isFullCapture is false, then this field will be the time offset between the capture start
+ // time and the timestamp of this sample.
+ optional int64 timestamp_offset_ms = 66;
}
message ContentionTimeStats {
@@ -3169,6 +3179,13 @@ message WifiUsabilityStatsTraining {
optional int64 capture_start_timestamp_secs = 2;
optional TrainingData training_data = 3;
+
+ // If isFullCapture is true in the WifiManager.storeCaptureData call, this represents the time
+ // offset between the last sample in the capture buffer and the time the capture buffer was
+ // stored. If ring buffer is empty (no last sample), we set store_time_offset_ms to 0.
+ // If isFullCapture is false, this represents the time between 'capture period stop time' and the
+ // time the capture buffer was stored.
+ optional int64 store_time_offset_ms = 4;
}
message DeviceMobilityStatePnoScanStats {
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
index e317d31dbe..08abfbc4e5 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
@@ -166,6 +166,7 @@ import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiIsUnusableEvent;
import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiRadioUsage;
import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiUsabilityStats;
import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiUsabilityStatsEntry;
+import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiUsabilityStatsTraining;
import com.android.server.wifi.rtt.RttMetrics;
import com.android.server.wifi.util.InformationElementUtil;
import com.android.wifi.flags.Flags;
@@ -186,6 +187,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.Duration;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
@@ -219,7 +221,6 @@ public class WifiMetricsTest extends WifiBaseTest {
private static final int TEST_CHANNEL = 36;
private static final int POLLING_INTERVAL_DEFAULT = 3000;
private static final int POLLING_INTERVAL_NOT_DEFAULT = 6000;
-
private MockitoSession mSession;
@Mock Context mContext;
MockResources mResources;
@@ -329,7 +330,6 @@ public class WifiMetricsTest extends WifiBaseTest {
when(mWifiInfo.getFrequency()).thenReturn(5850);
when(mWifiInfo.getBSSID()).thenReturn("5G_WiFi");
when(mWifiInfo.getRssi()).thenReturn(-55);
-
}
@After
@@ -4355,6 +4355,198 @@ public class WifiMetricsTest extends WifiBaseTest {
}
/**
+ * When ring buffer is empty, verify that full-capture will capture empty results
+ */
+ @Test
+ public void testStoreCapturedDataEmptyRingbufferFullCapture() throws Exception {
+ Instant testCurrentInstant =
+ Instant.parse("2024-01-01T00:00:00Z").plus(Duration.ofSeconds(258));
+ when(mClock.getCurrentInstant()).thenReturn(testCurrentInstant);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn((long) 10
+ * mWifiMetrics.MILLIS_IN_A_SECOND);
+ assertEquals(0, mWifiMetrics.mWifiUsabilityStatsEntriesRingBuffer.size());
+ mWifiMetrics.storeCapturedData(123, true, 2 * mWifiMetrics.MILLIS_IN_A_SECOND,
+ 8 * mWifiMetrics.MILLIS_IN_A_SECOND);
+ dumpProtoAndDeserialize();
+ assertEquals(1, mDecodedProto.wifiUsabilityStatsTraining.length);
+ assertEquals(123, mDecodedProto.wifiUsabilityStatsTraining[0].dataCaptureType);
+ assertEquals(1704067200,
+ mDecodedProto.wifiUsabilityStatsTraining[0].captureStartTimestampSecs);
+ assertEquals(0, mDecodedProto.wifiUsabilityStatsTraining[0].trainingData.stats.length);
+ assertEquals(0, mDecodedProto.wifiUsabilityStatsTraining[0].storeTimeOffsetMs);
+ }
+
+ /**
+ * When ring buffer is empty, verify that non full-capture will capture empty results
+ */
+ @Test
+ public void testStoreCapturedDataEmptyRingbufferNonFullCapture() throws Exception {
+ Instant testCurrentInstant =
+ Instant.parse("2024-01-01T00:00:00Z").plus(Duration.ofSeconds(258));
+ when(mClock.getCurrentInstant()).thenReturn(testCurrentInstant);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn((long) 258
+ * mWifiMetrics.MILLIS_IN_A_SECOND);
+ assertEquals(0, mWifiMetrics.mWifiUsabilityStatsEntriesRingBuffer.size());
+ mWifiMetrics.storeCapturedData(123, false, 2 * mWifiMetrics.MILLIS_IN_A_SECOND,
+ 8 * mWifiMetrics.MILLIS_IN_A_SECOND);
+ dumpProtoAndDeserialize();
+ assertEquals(1, mDecodedProto.wifiUsabilityStatsTraining.length);
+ assertEquals(123, mDecodedProto.wifiUsabilityStatsTraining[0].dataCaptureType);
+ assertEquals(1704067200,
+ mDecodedProto.wifiUsabilityStatsTraining[0].captureStartTimestampSecs);
+ assertEquals(0, mDecodedProto.wifiUsabilityStatsTraining[0].trainingData.stats.length);
+ // 258 (current time) - 8 (triggerStopTimeMillis) = 250
+ assertEquals(250 * mWifiMetrics.MILLIS_IN_A_SECOND,
+ mDecodedProto.wifiUsabilityStatsTraining[0].storeTimeOffsetMs);
+ }
+
+ private void ringBufferSetupForTestStoreCapturedData() {
+ // Starting from 20s, add a WifiUsabilityStatsEntry into ring buffer every 3s,
+ // the last timestamp is 20 + 3 * (80-1) = 257s
+ for (int i = 0; i < mWifiMetrics.MAX_WIFI_USABILITY_STATS_ENTRIES_RING_BUFFER_SIZE; ++i) {
+ WifiUsabilityStatsEntry entry = new WifiUsabilityStatsEntry();
+ entry.timeStampMs = (20 + i * 3) * mWifiMetrics.MILLIS_IN_A_SECOND;
+ mWifiMetrics.mWifiUsabilityStatsEntriesRingBuffer.add(entry);
+ }
+ assertEquals(80, mWifiMetrics.mWifiUsabilityStatsEntriesRingBuffer.size());
+ assertEquals(0, mWifiMetrics.mWifiUsabilityStatsTrainingExamples.size());
+ // Set current time since boot to 258s
+ when(mClock.getElapsedSinceBootMillis()).thenReturn((long) 258
+ * mWifiMetrics.MILLIS_IN_A_SECOND);
+ // Assume device boot up time is 2024-01-01, 00:00:00 UTC, unix timestamp in seconds
+ // is 1704067200
+ Instant testCurrentInstant =
+ Instant.parse("2024-01-01T00:00:00Z").plus(Duration.ofSeconds(258));
+ when(mClock.getCurrentInstant()).thenReturn(testCurrentInstant);
+ }
+
+ /**
+ * In non full-capture, verify:
+ * triggerStartTimeMillis has to be positive
+ */
+ @Test
+ public void testStoreCapturedDataNonFullCaptureStartTimePositive() throws Exception {
+ ringBufferSetupForTestStoreCapturedData();
+ mWifiMetrics.storeCapturedData(1, false, -1 * mWifiMetrics.MILLIS_IN_A_SECOND,
+ 20 * mWifiMetrics.MILLIS_IN_A_SECOND);
+ dumpProtoAndDeserialize();
+ assertEquals(0, mDecodedProto.wifiUsabilityStatsTraining.length);
+ }
+
+ /**
+ * In non full-capture, verify:
+ * triggerStopTimeMillis has to be positive
+ */
+ @Test
+ public void testStoreCapturedDataNonFullCaptureStopTimePositive() throws Exception {
+ ringBufferSetupForTestStoreCapturedData();
+ mWifiMetrics.storeCapturedData(1, false, 30 * mWifiMetrics.MILLIS_IN_A_SECOND,
+ -1 * mWifiMetrics.MILLIS_IN_A_SECOND);
+ dumpProtoAndDeserialize();
+ assertEquals(0, mDecodedProto.wifiUsabilityStatsTraining.length);
+ }
+
+ /**
+ * In non full-capture, verify:
+ * triggerStartTimeMillis must be smaller than triggerStopTimeMillis
+ */
+ @Test
+ public void testStoreCapturedDataNonFullCaptureStartTimeEalierThanStopTime() throws Exception {
+ ringBufferSetupForTestStoreCapturedData();
+ mWifiMetrics.storeCapturedData(1, false, 30 * mWifiMetrics.MILLIS_IN_A_SECOND,
+ 20 * mWifiMetrics.MILLIS_IN_A_SECOND);
+ dumpProtoAndDeserialize();
+ assertEquals(0, mDecodedProto.wifiUsabilityStatsTraining.length);
+ }
+
+ /**
+ * In non full-capture, verify results
+ */
+ @Test
+ public void testStoreCapturedDataNonFullCapture() throws Exception {
+ ringBufferSetupForTestStoreCapturedData();
+ // Do a successful capture in [30s, 150s], and verify each field
+ mWifiMetrics.storeCapturedData(1, false, 30 * mWifiMetrics.MILLIS_IN_A_SECOND,
+ 150 * mWifiMetrics.MILLIS_IN_A_SECOND);
+ dumpProtoAndDeserialize();
+ assertEquals(1, mDecodedProto.wifiUsabilityStatsTraining.length);
+ WifiUsabilityStatsTraining result = mDecodedProto.wifiUsabilityStatsTraining[0];
+ assertEquals(1, result.dataCaptureType);
+ assertEquals(1704067200, result.captureStartTimestampSecs);
+ // 258 (current time) - 150 (triggerStopTimeMillis) = 108
+ assertEquals(108 * mWifiMetrics.MILLIS_IN_A_SECOND, result.storeTimeOffsetMs);
+ // Capture period is 150 - 30 = 120s, 120 / 3 = 40 WifiUsabilityStatsEntries
+ assertEquals(40, result.trainingData.stats.length);
+ for (int i = 0; i < 40; ++i) {
+ WifiUsabilityStatsEntry resultEntry = result.trainingData.stats[i];
+ assertEquals(0, resultEntry.timeStampMs);
+ // The timestamp of WifiUsabilityStatsEntries who are in captured result are:
+ // 32, 35, ... 149
+ assertEquals((2 + 3 * i) * mWifiMetrics.MILLIS_IN_A_SECOND,
+ resultEntry.timestampOffsetMs);
+ }
+ }
+
+ /**
+ * In full-capture, verify results
+ */
+ @Test
+ public void testStoreCapturedDataFullCapture() throws Exception {
+ ringBufferSetupForTestStoreCapturedData();
+ // Do a successful full-capture, and verify each field
+ mWifiMetrics.storeCapturedData(2, true, 30 * mWifiMetrics.MILLIS_IN_A_SECOND,
+ 150 * mWifiMetrics.MILLIS_IN_A_SECOND);
+ dumpProtoAndDeserialize();
+ assertEquals(1, mDecodedProto.wifiUsabilityStatsTraining.length);
+ WifiUsabilityStatsTraining result = mDecodedProto.wifiUsabilityStatsTraining[0];
+ assertEquals(2, result.dataCaptureType);
+ assertEquals(1704067200, result.captureStartTimestampSecs);
+ // 258 (current time) - 257 (triggerStopTimeMillis) = 1
+ assertEquals(1 * mWifiMetrics.MILLIS_IN_A_SECOND, result.storeTimeOffsetMs);
+ // Capture period is 257 - 20 = 237s, (237 / 3) + 1 = 80 WifiUsabilityStatsEntries
+ assertEquals(mWifiMetrics.MAX_WIFI_USABILITY_STATS_ENTRIES_RING_BUFFER_SIZE,
+ result.trainingData.stats.length);
+ for (int i = 0; i < mWifiMetrics.MAX_WIFI_USABILITY_STATS_ENTRIES_RING_BUFFER_SIZE; ++i) {
+ WifiUsabilityStatsEntry resultEntry = result.trainingData.stats[i];
+ assertEquals(0, resultEntry.timeStampMs);
+ // The timestamps of WifiUsabilityStatsEntries who are in captured result are:
+ // 20, 23, ... 257, offsets are 0, 3, ... 237
+ assertEquals((3 * i) * mWifiMetrics.MILLIS_IN_A_SECOND,
+ resultEntry.timestampOffsetMs);
+ }
+ }
+
+ /**
+ * Verify wifiUsabilityStatsTraining size limit
+ */
+ @Test
+ public void testwifiUsabilityStatsTrainingSize() {
+ ringBufferSetupForTestStoreCapturedData();
+ // Do MAX_WIFI_USABILITY_STATS_TRAINING_SIZE times successful data capture
+ for (int i = 0; i < WifiMetrics.MAX_WIFI_USABILITY_STATS_TRAINING_SIZE; ++i) {
+ mWifiMetrics.storeCapturedData(2, false, (30 + i * 3) * mWifiMetrics.MILLIS_IN_A_SECOND,
+ (150 + i * 3) * mWifiMetrics.MILLIS_IN_A_SECOND);
+ }
+ assertEquals(WifiMetrics.MAX_WIFI_USABILITY_STATS_TRAINING_SIZE,
+ mWifiMetrics.mWifiUsabilityStatsTrainingExamples.size());
+ // 1st capture period is [30s, 150s), current time is 258s, storeTimeOffsetMs is 108s
+ assertEquals(108 * mWifiMetrics.MILLIS_IN_A_SECOND,
+ mWifiMetrics.mWifiUsabilityStatsTrainingExamples.get(0).storeTimeOffsetMs);
+
+ // Do another successful data capture, the size should not grow
+ mWifiMetrics.storeCapturedData(2, false,
+ (30 + WifiMetrics.MAX_WIFI_USABILITY_STATS_TRAINING_SIZE)
+ * mWifiMetrics.MILLIS_IN_A_SECOND,
+ (150 + WifiMetrics.MAX_WIFI_USABILITY_STATS_TRAINING_SIZE)
+ * mWifiMetrics.MILLIS_IN_A_SECOND);
+ assertEquals(WifiMetrics.MAX_WIFI_USABILITY_STATS_TRAINING_SIZE,
+ mWifiMetrics.mWifiUsabilityStatsTrainingExamples.size());
+ // 1st capture period is [33s, 153s), current time is 258s, storeTimeOffsetMs is 105s
+ assertEquals(105 * mWifiMetrics.MILLIS_IN_A_SECOND,
+ mWifiMetrics.mWifiUsabilityStatsTrainingExamples.get(0).storeTimeOffsetMs);
+ }
+
+ /**
* Verify that updateWifiUsabilityStatsEntries correctly converts the inputs into
* a WifiUsabilityStatsEntry Object and then stores it.
*