Create KeepaliveStatsTracker for Duration metrics.
Create a new class to store duration metrics of AutomaticOnOffKeepalives
for the DurationPerNumOfKeepalive proto.
This tracks the duration of time that the AutomaticOnOffKeepaliveTracker
contains a given number of concurrent keepalives and the duration of
time that it contains a given number of active concurrent keepalives.
Bug: 273451360
Test: atest FrameworksNetTests
Change-Id: If31ccbc7b02e6ad3ff1646e92e55ca7c8662e47c
diff --git a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
index 881c92d..ee8ab68 100644
--- a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
@@ -177,6 +177,7 @@
private static final int MAX_EVENTS_LOGS = 40;
private final LocalLog mEventLog = new LocalLog(MAX_EVENTS_LOGS);
+ private final KeepaliveStatsTracker mKeepaliveStatsTracker = new KeepaliveStatsTracker();
/**
* Information about a managed keepalive.
*
@@ -421,6 +422,7 @@
public void handleStartKeepalive(Message message) {
final AutomaticOnOffKeepalive autoKi = (AutomaticOnOffKeepalive) message.obj;
mEventLog.log("Start keepalive " + autoKi.mCallback + " on " + autoKi.getNetwork());
+ mKeepaliveStatsTracker.onStartKeepalive();
mKeepaliveTracker.handleStartKeepalive(autoKi.mKi);
// Add automatic on/off request into list to track its life cycle.
@@ -438,12 +440,14 @@
}
private void handleResumeKeepalive(@NonNull final KeepaliveTracker.KeepaliveInfo ki) {
+ mKeepaliveStatsTracker.onResumeKeepalive();
mKeepaliveTracker.handleStartKeepalive(ki);
mEventLog.log("Resumed successfully keepalive " + ki.mCallback + " on " + ki.mNai);
}
private void handlePauseKeepalive(@NonNull final KeepaliveTracker.KeepaliveInfo ki) {
mEventLog.log("Suspend keepalive " + ki.mCallback + " on " + ki.mNai);
+ mKeepaliveStatsTracker.onPauseKeepalive();
// TODO : mKT.handleStopKeepalive should take a KeepaliveInfo instead
mKeepaliveTracker.handleStopKeepalive(ki.getNai(), ki.getSlot(), SUCCESS_PAUSED);
}
@@ -467,6 +471,7 @@
private void cleanupAutoOnOffKeepalive(@NonNull final AutomaticOnOffKeepalive autoKi) {
ensureRunningOnHandlerThread();
+ mKeepaliveStatsTracker.onStopKeepalive(autoKi.mAutomaticOnOffState != STATE_SUSPENDED);
autoKi.close();
if (null != autoKi.mAlarmListener) mAlarmManager.cancel(autoKi.mAlarmListener);
diff --git a/service/src/com/android/server/connectivity/KeepaliveStatsTracker.java b/service/src/com/android/server/connectivity/KeepaliveStatsTracker.java
new file mode 100644
index 0000000..c461809
--- /dev/null
+++ b/service/src/com/android/server/connectivity/KeepaliveStatsTracker.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.metrics.DurationForNumOfKeepalive;
+import com.android.metrics.DurationPerNumOfKeepalive;
+
+import java.util.ArrayList;
+import java.util.List;
+
+// TODO(b/273451360): Also track KeepaliveLifetimeForCarrier and DailykeepaliveInfoReported
+/**
+ * Tracks carrier and duration metrics of automatic on/off keepalives.
+ *
+ * <p>This class follows AutomaticOnOffKeepaliveTracker closely and its on*Keepalive methods needs
+ * to be called in a timely manner to keep the metrics accurate. It is also not thread-safe and all
+ * public methods must be called by the same thread, namely the ConnectivityService handler thread.
+ */
+public class KeepaliveStatsTracker {
+ private static final String TAG = KeepaliveStatsTracker.class.getSimpleName();
+
+ private final Dependencies mDependencies;
+ // List of duration stats metric where the index is the number of concurrent keepalives.
+ // Each DurationForNumOfKeepalive message stores a registered duration and an active duration.
+ // Registered duration is the total time spent with mNumRegisteredKeepalive == index.
+ // Active duration is the total time spent with mNumActiveKeepalive == index.
+ private final List<DurationForNumOfKeepalive.Builder> mDurationPerNumOfKeepalive =
+ new ArrayList<>();
+
+ private int mNumRegisteredKeepalive = 0;
+ private int mNumActiveKeepalive = 0;
+
+ // A timestamp of the most recent time the duration metrics was updated.
+ private long mTimestampSinceLastUpdateDurations;
+
+ /** Dependency class */
+ @VisibleForTesting
+ public static class Dependencies {
+ // Returns a timestamp with the time base of SystemClock.uptimeMillis to keep durations
+ // relative to start time and avoid timezone change.
+ public long getUptimeMillis() {
+ return SystemClock.uptimeMillis();
+ }
+ }
+
+ public KeepaliveStatsTracker() {
+ this(new Dependencies());
+ }
+
+ @VisibleForTesting
+ public KeepaliveStatsTracker(Dependencies dependencies) {
+ mDependencies = dependencies;
+ mTimestampSinceLastUpdateDurations = mDependencies.getUptimeMillis();
+ }
+
+ /** Ensures the list of duration metrics is large enough for number of registered keepalives. */
+ private void ensureDurationPerNumOfKeepaliveSize() {
+ if (mNumActiveKeepalive < 0 || mNumRegisteredKeepalive < 0) {
+ throw new IllegalStateException(
+ "Number of active or registered keepalives is negative");
+ }
+ if (mNumActiveKeepalive > mNumRegisteredKeepalive) {
+ throw new IllegalStateException(
+ "Number of active keepalives greater than registered keepalives");
+ }
+
+ while (mDurationPerNumOfKeepalive.size() <= mNumRegisteredKeepalive) {
+ final DurationForNumOfKeepalive.Builder durationForNumOfKeepalive =
+ DurationForNumOfKeepalive.newBuilder();
+ durationForNumOfKeepalive.setNumOfKeepalive(mDurationPerNumOfKeepalive.size());
+ durationForNumOfKeepalive.setKeepaliveRegisteredDurationsMsec(0);
+ durationForNumOfKeepalive.setKeepaliveActiveDurationsMsec(0);
+
+ mDurationPerNumOfKeepalive.add(durationForNumOfKeepalive);
+ }
+ }
+
+ /**
+ * Updates the durations metrics to the given time. This should always be called before making a
+ * change to mNumRegisteredKeepalive or mNumActiveKeepalive to keep the duration metrics
+ * correct.
+ *
+ * @param timeNow a timestamp obtained using Dependencies.getUptimeMillis
+ */
+ private void updateDurationsPerNumOfKeepalive(long timeNow) {
+ if (mDurationPerNumOfKeepalive.size() < mNumRegisteredKeepalive) {
+ Log.e(TAG, "Unexpected jump in number of registered keepalive");
+ }
+ ensureDurationPerNumOfKeepaliveSize();
+
+ final int durationIncrease = (int) (timeNow - mTimestampSinceLastUpdateDurations);
+ final DurationForNumOfKeepalive.Builder durationForNumOfRegisteredKeepalive =
+ mDurationPerNumOfKeepalive.get(mNumRegisteredKeepalive);
+
+ durationForNumOfRegisteredKeepalive.setKeepaliveRegisteredDurationsMsec(
+ durationForNumOfRegisteredKeepalive.getKeepaliveRegisteredDurationsMsec()
+ + durationIncrease);
+
+ final DurationForNumOfKeepalive.Builder durationForNumOfActiveKeepalive =
+ mDurationPerNumOfKeepalive.get(mNumActiveKeepalive);
+
+ durationForNumOfActiveKeepalive.setKeepaliveActiveDurationsMsec(
+ durationForNumOfActiveKeepalive.getKeepaliveActiveDurationsMsec()
+ + durationIncrease);
+
+ mTimestampSinceLastUpdateDurations = timeNow;
+ }
+
+ /** Inform the KeepaliveStatsTracker a keepalive has just started and is active. */
+ public void onStartKeepalive() {
+ final long timeNow = mDependencies.getUptimeMillis();
+ updateDurationsPerNumOfKeepalive(timeNow);
+
+ mNumRegisteredKeepalive++;
+ mNumActiveKeepalive++;
+ }
+
+ /** Inform the KeepaliveStatsTracker a keepalive has just been paused. */
+ public void onPauseKeepalive() {
+ final long timeNow = mDependencies.getUptimeMillis();
+ updateDurationsPerNumOfKeepalive(timeNow);
+
+ mNumActiveKeepalive--;
+ }
+
+ /** Inform the KeepaliveStatsTracker a keepalive has just been resumed. */
+ public void onResumeKeepalive() {
+ final long timeNow = mDependencies.getUptimeMillis();
+ updateDurationsPerNumOfKeepalive(timeNow);
+
+ mNumActiveKeepalive++;
+ }
+
+ /** Inform the KeepaliveStatsTracker a keepalive has just been stopped. */
+ public void onStopKeepalive(boolean wasActive) {
+ final long timeNow = mDependencies.getUptimeMillis();
+ updateDurationsPerNumOfKeepalive(timeNow);
+
+ mNumRegisteredKeepalive--;
+ if (wasActive) mNumActiveKeepalive--;
+ }
+
+ /**
+ * Builds and returns DurationPerNumOfKeepalive proto
+ *
+ * <p>TODO(b/273451360): Write DailykeepaliveInfoReported metrics to statsd
+ */
+ public DurationPerNumOfKeepalive buildKeepaliveMetrics() {
+ final long timeNow = mDependencies.getUptimeMillis();
+ updateDurationsPerNumOfKeepalive(timeNow);
+
+ final DurationPerNumOfKeepalive.Builder durationPerNumOfKeepalive =
+ DurationPerNumOfKeepalive.newBuilder();
+
+ mDurationPerNumOfKeepalive.forEach(
+ durationForNumOfKeepalive ->
+ durationPerNumOfKeepalive.addDurationForNumOfKeepalive(
+ durationForNumOfKeepalive));
+
+ return durationPerNumOfKeepalive.build();
+ }
+
+ /** Resets the stored metrics but maintains the state of keepalives */
+ public void resetMetrics() {
+ mDurationPerNumOfKeepalive.clear();
+ ensureDurationPerNumOfKeepaliveSize();
+ }
+}
diff --git a/tests/unit/java/com/android/server/connectivity/KeepaliveStatsTrackerTest.java b/tests/unit/java/com/android/server/connectivity/KeepaliveStatsTrackerTest.java
new file mode 100644
index 0000000..8837ba1
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivity/KeepaliveStatsTrackerTest.java
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doReturn;
+
+import android.os.Build;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.metrics.DurationForNumOfKeepalive;
+import com.android.metrics.DurationPerNumOfKeepalive;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(DevSdkIgnoreRunner.class)
+@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+public class KeepaliveStatsTrackerTest {
+
+ private KeepaliveStatsTracker mKeepaliveStatsTracker;
+ @Mock KeepaliveStatsTracker.Dependencies mDependencies;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ setUptimeMillis(0);
+ mKeepaliveStatsTracker = new KeepaliveStatsTracker(mDependencies);
+ }
+
+ private void setUptimeMillis(long time) {
+ doReturn(time).when(mDependencies).getUptimeMillis();
+ }
+
+ /**
+ * Asserts that a DurationPerNumOfKeepalive contains expected values
+ *
+ * @param expectRegisteredDurations integer array where the index is the number of concurrent
+ * keepalives and the value is the expected duration of time that the tracker is in a state
+ * with the given number of keepalives registered.
+ * @param expectActiveDurations integer array where the index is the number of concurrent
+ * keepalives and the value is the expected duration of time that the tracker is in a state
+ * with the given number of keepalives active.
+ * @param resultDurationsPerNumOfKeepalive the DurationPerNumOfKeepalive message to assert.
+ */
+ private void assertDurationMetrics(
+ int[] expectRegisteredDurations,
+ int[] expectActiveDurations,
+ DurationPerNumOfKeepalive resultDurationsPerNumOfKeepalive) {
+ final int maxNumOfKeepalive = expectRegisteredDurations.length;
+ assertEquals(maxNumOfKeepalive, expectActiveDurations.length);
+ assertEquals(
+ maxNumOfKeepalive,
+ resultDurationsPerNumOfKeepalive.getDurationForNumOfKeepaliveCount());
+ for (int numOfKeepalive = 0; numOfKeepalive < maxNumOfKeepalive; numOfKeepalive++) {
+ final DurationForNumOfKeepalive resultDurations =
+ resultDurationsPerNumOfKeepalive.getDurationForNumOfKeepalive(numOfKeepalive);
+
+ assertEquals(numOfKeepalive, resultDurations.getNumOfKeepalive());
+ assertEquals(
+ expectRegisteredDurations[numOfKeepalive],
+ resultDurations.getKeepaliveRegisteredDurationsMsec());
+ assertEquals(
+ expectActiveDurations[numOfKeepalive],
+ resultDurations.getKeepaliveActiveDurationsMsec());
+ }
+ }
+
+ @Test
+ public void testNoKeepalive() {
+ final int writeTime = 5000;
+
+ setUptimeMillis(writeTime);
+ final DurationPerNumOfKeepalive resultDurations =
+ mKeepaliveStatsTracker.buildKeepaliveMetrics();
+
+ // Expect that the durations are all in numOfKeepalive = 0.
+ final int[] expectRegisteredDurations = new int[] {writeTime};
+ final int[] expectActiveDurations = new int[] {writeTime};
+ assertDurationMetrics(expectRegisteredDurations, expectActiveDurations, resultDurations);
+ }
+
+ /*
+ * Diagram of test (not to scale):
+ * Key: S - Start/Stop, P - Pause, R - Resume, W - Write
+ *
+ * Keepalive S W
+ * Timeline |------------------------------|
+ */
+ @Test
+ public void testOneKeepalive_startOnly() {
+ final int startTime = 1000;
+ final int writeTime = 5000;
+
+ setUptimeMillis(startTime);
+ mKeepaliveStatsTracker.onStartKeepalive();
+
+ setUptimeMillis(writeTime);
+ final DurationPerNumOfKeepalive resultDurations =
+ mKeepaliveStatsTracker.buildKeepaliveMetrics();
+
+ // The keepalive is never stopped, expect the duration for numberOfKeepalive of 1 to range
+ // from startTime to writeTime.
+ final int[] expectRegisteredDurations = new int[] {startTime, writeTime - startTime};
+ final int[] expectActiveDurations = new int[] {startTime, writeTime - startTime};
+ assertDurationMetrics(expectRegisteredDurations, expectActiveDurations, resultDurations);
+ }
+
+ /*
+ * Diagram of test (not to scale):
+ * Key: S - Start/Stop, P - Pause, R - Resume, W - Write
+ *
+ * Keepalive S P W
+ * Timeline |------------------------------|
+ */
+ @Test
+ public void testOneKeepalive_paused() {
+ final int startTime = 1000;
+ final int pauseTime = 2030;
+ final int writeTime = 5000;
+
+ setUptimeMillis(startTime);
+ mKeepaliveStatsTracker.onStartKeepalive();
+
+ setUptimeMillis(pauseTime);
+ mKeepaliveStatsTracker.onPauseKeepalive();
+
+ setUptimeMillis(writeTime);
+ final DurationPerNumOfKeepalive resultDurations =
+ mKeepaliveStatsTracker.buildKeepaliveMetrics();
+
+ // The keepalive is paused but not stopped, expect the registered duration for
+ // numberOfKeepalive of 1 to still range from startTime to writeTime while the active
+ // duration stops at pauseTime.
+ final int[] expectRegisteredDurations = new int[] {startTime, writeTime - startTime};
+ final int[] expectActiveDurations =
+ new int[] {startTime + (writeTime - pauseTime), pauseTime - startTime};
+ assertDurationMetrics(expectRegisteredDurations, expectActiveDurations, resultDurations);
+ }
+
+ /*
+ * Diagram of test (not to scale):
+ * Key: S - Start/Stop, P - Pause, R - Resume, W - Write
+ *
+ * Keepalive S P R W
+ * Timeline |------------------------------|
+ */
+ @Test
+ public void testOneKeepalive_resumed() {
+ final int startTime = 1000;
+ final int pauseTime = 2030;
+ final int resumeTime = 3450;
+ final int writeTime = 5000;
+
+ setUptimeMillis(startTime);
+ mKeepaliveStatsTracker.onStartKeepalive();
+
+ setUptimeMillis(pauseTime);
+ mKeepaliveStatsTracker.onPauseKeepalive();
+
+ setUptimeMillis(resumeTime);
+ mKeepaliveStatsTracker.onResumeKeepalive();
+
+ setUptimeMillis(writeTime);
+ final DurationPerNumOfKeepalive resultDurations =
+ mKeepaliveStatsTracker.buildKeepaliveMetrics();
+
+ // The keepalive is paused and resumed but not stopped, expect the registered duration for
+ // numberOfKeepalive of 1 to still range from startTime to writeTime while the active
+ // duration stops at pauseTime but resumes at resumeTime and stops at writeTime.
+ final int[] expectRegisteredDurations = new int[] {startTime, writeTime - startTime};
+ final int[] expectActiveDurations =
+ new int[] {
+ startTime + (resumeTime - pauseTime),
+ (pauseTime - startTime) + (writeTime - resumeTime)
+ };
+ assertDurationMetrics(expectRegisteredDurations, expectActiveDurations, resultDurations);
+ }
+
+ /*
+ * Diagram of test (not to scale):
+ * Key: S - Start/Stop, P - Pause, R - Resume, W - Write
+ *
+ * Keepalive S P R S W
+ * Timeline |------------------------------|
+ */
+ @Test
+ public void testOneKeepalive_stopped() {
+ final int startTime = 1000;
+ final int pauseTime = 2930;
+ final int resumeTime = 3452;
+ final int stopTime = 4157;
+ final int writeTime = 5000;
+
+ setUptimeMillis(startTime);
+ mKeepaliveStatsTracker.onStartKeepalive();
+
+ setUptimeMillis(pauseTime);
+ mKeepaliveStatsTracker.onPauseKeepalive();
+
+ setUptimeMillis(resumeTime);
+ mKeepaliveStatsTracker.onResumeKeepalive();
+
+ setUptimeMillis(stopTime);
+ mKeepaliveStatsTracker.onStopKeepalive(/* wasActive= */ true);
+
+ setUptimeMillis(writeTime);
+ final DurationPerNumOfKeepalive resultDurations =
+ mKeepaliveStatsTracker.buildKeepaliveMetrics();
+
+ // The keepalive is now stopped, expect the registered duration for numberOfKeepalive of 1
+ // to now range from startTime to stopTime while the active duration stops at pauseTime but
+ // resumes at resumeTime and stops again at stopTime.
+ final int[] expectRegisteredDurations =
+ new int[] {startTime + (writeTime - stopTime), stopTime - startTime};
+ final int[] expectActiveDurations =
+ new int[] {
+ startTime + (resumeTime - pauseTime) + (writeTime - stopTime),
+ (pauseTime - startTime) + (stopTime - resumeTime)
+ };
+ assertDurationMetrics(expectRegisteredDurations, expectActiveDurations, resultDurations);
+ }
+
+ /*
+ * Diagram of test (not to scale):
+ * Key: S - Start/Stop, P - Pause, R - Resume, W - Write
+ *
+ * Keepalive S P S W
+ * Timeline |------------------------------|
+ */
+ @Test
+ public void testOneKeepalive_pausedStopped() {
+ final int startTime = 1000;
+ final int pauseTime = 2930;
+ final int stopTime = 4157;
+ final int writeTime = 5000;
+
+ setUptimeMillis(startTime);
+ mKeepaliveStatsTracker.onStartKeepalive();
+
+ setUptimeMillis(pauseTime);
+ mKeepaliveStatsTracker.onPauseKeepalive();
+
+ setUptimeMillis(stopTime);
+ mKeepaliveStatsTracker.onStopKeepalive(/* wasActive= */ false);
+
+ setUptimeMillis(writeTime);
+ final DurationPerNumOfKeepalive resultDurations =
+ mKeepaliveStatsTracker.buildKeepaliveMetrics();
+
+ // The keepalive is stopped while paused, expect the registered duration for
+ // numberOfKeepalive of 1 to range from startTime to stopTime while the active duration
+ // simply stops at pauseTime.
+ final int[] expectRegisteredDurations =
+ new int[] {startTime + (writeTime - stopTime), stopTime - startTime};
+ final int[] expectActiveDurations =
+ new int[] {startTime + (writeTime - pauseTime), (pauseTime - startTime)};
+ assertDurationMetrics(expectRegisteredDurations, expectActiveDurations, resultDurations);
+ }
+
+ /*
+ * Diagram of test (not to scale):
+ * Key: S - Start/Stop, P - Pause, R - Resume, W - Write
+ *
+ * Keepalive S P R P R P R S W
+ * Timeline |------------------------------|
+ */
+ @Test
+ public void testOneKeepalive_multiplePauses() {
+ final int startTime = 1000;
+ // Alternating timestamps of pause and resume
+ final int[] pauseResumeTimes = new int[] {1200, 1400, 1700, 2000, 2400, 2800};
+ final int stopTime = 4000;
+ final int writeTime = 5000;
+
+ setUptimeMillis(startTime);
+ mKeepaliveStatsTracker.onStartKeepalive();
+
+ for (int i = 0; i < pauseResumeTimes.length; i++) {
+ setUptimeMillis(pauseResumeTimes[i]);
+ if (i % 2 == 0) {
+ mKeepaliveStatsTracker.onPauseKeepalive();
+ } else {
+ mKeepaliveStatsTracker.onResumeKeepalive();
+ }
+ }
+
+ setUptimeMillis(stopTime);
+ mKeepaliveStatsTracker.onStopKeepalive(/* wasActive= */ true);
+
+ setUptimeMillis(writeTime);
+ final DurationPerNumOfKeepalive resultDurations =
+ mKeepaliveStatsTracker.buildKeepaliveMetrics();
+
+ final int[] expectRegisteredDurations =
+ new int[] {startTime + (writeTime - stopTime), stopTime - startTime};
+ final int[] expectActiveDurations =
+ new int[] {
+ startTime + /* sum of (Resume - Pause) */ (900) + (writeTime - stopTime),
+ (pauseResumeTimes[0] - startTime)
+ + /* sum of (Pause - Resume) */ (700)
+ + (stopTime - pauseResumeTimes[5])
+ };
+ assertDurationMetrics(expectRegisteredDurations, expectActiveDurations, resultDurations);
+ }
+
+ /*
+ * Diagram of test (not to scale):
+ * Key: S - Start/Stop, P - Pause, R - Resume, W - Write
+ *
+ * Keepalive1 S1 P1 R1 S1 W
+ * Keepalive2 S2 P2 R2 W
+ * Timeline |------------------------------|
+ */
+ @Test
+ public void testTwoKeepalives() {
+ // The suffix 1/2 indicates which keepalive it is referring to.
+ final int startTime1 = 1000;
+ final int pauseTime1 = 1500;
+ final int startTime2 = 2000;
+ final int resumeTime1 = 2500;
+ final int pauseTime2 = 3000;
+ final int resumeTime2 = 3500;
+ final int stopTime1 = 4157;
+ final int writeTime = 5000;
+
+ setUptimeMillis(startTime1);
+ mKeepaliveStatsTracker.onStartKeepalive();
+
+ setUptimeMillis(pauseTime1);
+ mKeepaliveStatsTracker.onPauseKeepalive();
+
+ setUptimeMillis(startTime2);
+ mKeepaliveStatsTracker.onStartKeepalive();
+
+ setUptimeMillis(resumeTime1);
+ mKeepaliveStatsTracker.onResumeKeepalive();
+
+ setUptimeMillis(pauseTime2);
+ mKeepaliveStatsTracker.onPauseKeepalive();
+
+ setUptimeMillis(resumeTime2);
+ mKeepaliveStatsTracker.onResumeKeepalive();
+
+ setUptimeMillis(stopTime1);
+ mKeepaliveStatsTracker.onStopKeepalive(/* wasActive= */ true);
+
+ setUptimeMillis(writeTime);
+ final DurationPerNumOfKeepalive resultDurations =
+ mKeepaliveStatsTracker.buildKeepaliveMetrics();
+
+ // With two keepalives, the number of concurrent keepalives can vary from 0-2 depending on
+ // both keepalive states.
+ final int[] expectRegisteredDurations =
+ new int[] {
+ startTime1,
+ // 1 registered keepalive before keepalive2 starts and after keepalive1 stops.
+ (startTime2 - startTime1) + (writeTime - stopTime1),
+ // 2 registered keepalives between keepalive2 start and keepalive1 stop.
+ stopTime1 - startTime2
+ };
+ final int[] expectActiveDurations =
+ new int[] {
+ // 0 active keepalives when keepalive1 is paused before keepalive2 starts.
+ startTime1 + (startTime2 - pauseTime1),
+ // 1 active keepalive before keepalive1 is paused.
+ (pauseTime1 - startTime1)
+ // before keepalive1 is resumed and after keepalive2 starts.
+ + (resumeTime1 - startTime2)
+ // during keepalive2 is paused since keepalive1 has been resumed.
+ + (resumeTime2 - pauseTime2)
+ // after keepalive1 stops since keepalive2 has been resumed.
+ + (writeTime - stopTime1),
+ // 2 active keepalives before keepalive2 is paused and before keepalive1 stops.
+ (pauseTime2 - resumeTime1) + (stopTime1 - resumeTime2)
+ };
+ assertDurationMetrics(expectRegisteredDurations, expectActiveDurations, resultDurations);
+ }
+
+ /*
+ * Diagram of test (not to scale):
+ * Key: S - Start/Stop, P - Pause, R - Resume, W - Write
+ *
+ * Keepalive S W(reset+W) S W
+ * Timeline |------------------------------|
+ */
+ @Test
+ public void testResetMetrics() {
+ final int startTime = 1000;
+ final int writeTime = 5000;
+ final int stopTime = 7000;
+ final int writeTime2 = 10000;
+
+ setUptimeMillis(startTime);
+ mKeepaliveStatsTracker.onStartKeepalive();
+
+ setUptimeMillis(writeTime);
+ final DurationPerNumOfKeepalive resultDurations =
+ mKeepaliveStatsTracker.buildKeepaliveMetrics();
+
+ // Same expect as testOneKeepalive_startOnly
+ final int[] expectRegisteredDurations = new int[] {startTime, writeTime - startTime};
+ final int[] expectActiveDurations = new int[] {startTime, writeTime - startTime};
+ assertDurationMetrics(expectRegisteredDurations, expectActiveDurations, resultDurations);
+
+ // Reset metrics
+ mKeepaliveStatsTracker.resetMetrics();
+
+ final DurationPerNumOfKeepalive resultDurations2 =
+ mKeepaliveStatsTracker.buildKeepaliveMetrics();
+ // Expect the stored durations to be 0 but still contain the number of keepalive = 1.
+ assertDurationMetrics(new int[] {0, 0}, new int[] {0, 0}, resultDurations2);
+
+ // Expect that the keepalive is still registered after resetting so it can be stopped.
+ setUptimeMillis(stopTime);
+ mKeepaliveStatsTracker.onStopKeepalive(/* wasActive= */ true);
+
+ setUptimeMillis(writeTime2);
+ final DurationPerNumOfKeepalive resultDurations3 =
+ mKeepaliveStatsTracker.buildKeepaliveMetrics();
+
+ final int[] expectRegisteredDurations2 =
+ new int[] {writeTime2 - stopTime, stopTime - writeTime};
+ final int[] expectActiveDurations2 =
+ new int[] {writeTime2 - stopTime, stopTime - writeTime};
+ assertDurationMetrics(expectRegisteredDurations2, expectActiveDurations2, resultDurations3);
+ }
+}