Add ConditionTimer to CurrentValueBucket struct
Add the update condition timer logic based on state, condition, and
active changes.
Bug: 166159095
Bug: 166158610
Bug: 167458087
Bug: 166639935
Bug: 165018838
Test: `m statsd`
Test: `m statsd_test`
Test: `m`
Test: `atest statsd_test`
Change-Id: If7f0f66d971c6c5d78b1981ff875e466adb19a10
Merged-In: Ibb065d64be3775c699583215f7f376a1a5492ac1
diff --git a/bin/src/condition/ConditionTimer.h b/bin/src/condition/ConditionTimer.h
index 442bc11..1fbe252 100644
--- a/bin/src/condition/ConditionTimer.h
+++ b/bin/src/condition/ConditionTimer.h
@@ -36,7 +36,7 @@
public:
explicit ConditionTimer(bool initCondition, int64_t bucketStartNs) : mCondition(initCondition) {
if (initCondition) {
- mLastConditionTrueTimestampNs = bucketStartNs;
+ mLastConditionChangeTimestampNs = bucketStartNs;
}
};
@@ -44,21 +44,46 @@
// When a new bucket is created, this value will be reset to 0.
int64_t mTimerNs = 0;
- // Last elapsed real timestamp when condition turned to true
- // When a new bucket is created and the condition is true, then the timestamp is set
- // to be the bucket start timestamp.
- int64_t mLastConditionTrueTimestampNs = 0;
+ // Last elapsed real timestamp when condition changed.
+ int64_t mLastConditionChangeTimestampNs = 0;
bool mCondition = false;
int64_t newBucketStart(int64_t nextBucketStartNs) {
if (mCondition) {
- mTimerNs += (nextBucketStartNs - mLastConditionTrueTimestampNs);
- mLastConditionTrueTimestampNs = nextBucketStartNs;
+ // Normally, the next bucket happens after the last condition
+ // change. In this case, add the time between the condition becoming
+ // true to the next bucket start time.
+ // Otherwise, the next bucket start time is before the last
+ // condition change time, this means that the condition was false at
+ // the bucket boundary before the condition became true, so the
+ // timer should not get updated and the last condition change time
+ // remains as is.
+ if (nextBucketStartNs >= mLastConditionChangeTimestampNs) {
+ mTimerNs += (nextBucketStartNs - mLastConditionChangeTimestampNs);
+ mLastConditionChangeTimestampNs = nextBucketStartNs;
+ }
+ } else if (mLastConditionChangeTimestampNs > nextBucketStartNs) {
+ // The next bucket start time is before the last condition change
+ // time, this means that the condition was true at the bucket
+ // boundary before the condition became false, so adjust the timer
+ // to match how long the condition was true to the bucket boundary.
+ // This means remove the amount the condition stayed true in the
+ // next bucket from the current bucket.
+ mTimerNs -= (mLastConditionChangeTimestampNs - nextBucketStartNs);
}
int64_t temp = mTimerNs;
mTimerNs = 0;
+
+ if (!mCondition && (mLastConditionChangeTimestampNs > nextBucketStartNs)) {
+ // The next bucket start time is before the last condition change
+ // time, this means that the condition was true at the bucket
+ // boundary and remained true in the next bucket up to the condition
+ // change to false, so adjust the timer to match how long the
+ // condition stayed true in the next bucket (now the current bucket).
+ mTimerNs = mLastConditionChangeTimestampNs - nextBucketStartNs;
+ }
return temp;
}
@@ -67,11 +92,10 @@
return;
}
mCondition = newCondition;
- if (newCondition) {
- mLastConditionTrueTimestampNs = timestampNs;
- } else {
- mTimerNs += (timestampNs - mLastConditionTrueTimestampNs);
+ if (newCondition == false) {
+ mTimerNs += (timestampNs - mLastConditionChangeTimestampNs);
}
+ mLastConditionChangeTimestampNs = timestampNs;
}
FRIEND_TEST(ConditionTimerTest, TestTimer_Inital_False);
@@ -80,4 +104,4 @@
} // namespace statsd
} // namespace os
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/bin/src/metrics/ValueMetricProducer.cpp b/bin/src/metrics/ValueMetricProducer.cpp
index ce9e8a4..e766289 100644
--- a/bin/src/metrics/ValueMetricProducer.cpp
+++ b/bin/src/metrics/ValueMetricProducer.cpp
@@ -345,7 +345,6 @@
protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_BUCKET_DROP_REASON, dropEvent.reason);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DROP_TIME,
(long long)(NanoToMillis(dropEvent.dropTimeNs)));
- ;
protoOutput->end(dropEventToken);
}
protoOutput->end(wrapperToken);
@@ -390,8 +389,11 @@
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
(long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
}
- // only write the condition timer value if the metric has a condition.
- if (mConditionTrackerIndex >= 0) {
+ // We only write the condition timer value if the metric has a
+ // condition and/or is sliced by state.
+ // If the metric is sliced by state, the condition timer value is
+ // also sliced by state to reflect time spent in that state.
+ if (mConditionTrackerIndex >= 0 || !mSlicedStateAtoms.empty()) {
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_TRUE_NS,
(long long)bucket.mConditionTrueNs);
}
@@ -498,6 +500,8 @@
// Let condition timer know of new active state.
mConditionTimer.onConditionChanged(mIsActive, eventTimeNs);
+
+ updateCurrentSlicedBucketConditionTimers(mIsActive, eventTimeNs);
}
void ValueMetricProducer::onConditionChangedLocked(const bool condition,
@@ -520,6 +524,8 @@
invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET);
mCondition = ConditionState::kUnknown;
mConditionTimer.onConditionChanged(mCondition, eventTimeNs);
+
+ updateCurrentSlicedBucketConditionTimers(mCondition, eventTimeNs);
return;
}
@@ -561,6 +567,29 @@
flushIfNeededLocked(eventTimeNs);
mConditionTimer.onConditionChanged(mCondition, eventTimeNs);
+
+ updateCurrentSlicedBucketConditionTimers(mCondition, eventTimeNs);
+}
+
+void ValueMetricProducer::updateCurrentSlicedBucketConditionTimers(bool newCondition,
+ int64_t eventTimeNs) {
+ if (mSlicedStateAtoms.empty()) {
+ return;
+ }
+
+ // Utilize the current state key of each DimensionsInWhat key to determine
+ // which condition timers to update.
+ //
+ // Assumes that the MetricDimensionKey exists in `mCurrentSlicedBucket`.
+ bool inPulledData;
+ for (const auto& [dimensionInWhatKey, dimensionInWhatInfo] : mCurrentBaseInfo) {
+ // If the new condition is true, turn ON the condition timer only if
+ // the DimensionInWhat key was present in the pulled data.
+ inPulledData = dimensionInWhatInfo.hasCurrentState;
+ mCurrentSlicedBucket[MetricDimensionKey(dimensionInWhatKey,
+ dimensionInWhatInfo.currentState)]
+ .conditionTimer.onConditionChanged(newCondition && inPulledData, eventTimeNs);
+ }
}
void ValueMetricProducer::prepareFirstBucketLocked() {
@@ -662,8 +691,8 @@
// 2. A superset of the current mStateChangePrimaryKey
// was not found in the new pulled data (i.e. not in mMatchedDimensionInWhatKeys)
// then we need to reset the base.
- for (auto& slice : mCurrentSlicedBucket) {
- const auto& whatKey = slice.first.getDimensionKeyInWhat();
+ for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) {
+ const auto& whatKey = metricDimensionKey.getDimensionKeyInWhat();
bool presentInPulledData =
mMatchedMetricDimensionKeys.find(whatKey) != mMatchedMetricDimensionKeys.end();
if (!presentInPulledData && whatKey.contains(mStateChangePrimaryKey.second)) {
@@ -671,6 +700,12 @@
for (auto& baseInfo : it->second.baseInfos) {
baseInfo.hasBase = false;
}
+ // Set to false when DimensionInWhat key is not present in a pull.
+ // Used in onMatchedLogEventInternalLocked() to ensure the condition
+ // timer is turned on the next pull when data is present.
+ it->second.hasCurrentState = false;
+ // Turn OFF condition timer for keys not present in pulled data.
+ currentValueBucket.conditionTimer.onConditionChanged(false, eventElapsedTimeNs);
}
}
mMatchedMetricDimensionKeys.clear();
@@ -833,21 +868,26 @@
return;
}
- DimensionsInWhatInfo& dimensionsInWhatInfo = mCurrentBaseInfo[whatKey];
+ const auto& returnVal =
+ mCurrentBaseInfo.emplace(whatKey, DimensionsInWhatInfo(getUnknownStateKey()));
+ DimensionsInWhatInfo& dimensionsInWhatInfo = returnVal.first->second;
+ const HashableDimensionKey oldStateKey = dimensionsInWhatInfo.currentState;
vector<BaseInfo>& baseInfos = dimensionsInWhatInfo.baseInfos;
if (baseInfos.size() < mFieldMatchers.size()) {
VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size());
baseInfos.resize(mFieldMatchers.size());
}
+ // Ensure we turn on the condition timer in the case where dimensions
+ // were missing on a previous pull due to a state change.
+ bool stateChange = oldStateKey != stateKey;
if (!dimensionsInWhatInfo.hasCurrentState) {
- dimensionsInWhatInfo.currentState = getUnknownStateKey();
+ stateChange = true;
dimensionsInWhatInfo.hasCurrentState = true;
}
// We need to get the intervals stored with the previous state key so we can
// close these value intervals.
- const auto oldStateKey = dimensionsInWhatInfo.currentState;
vector<Interval>& intervals =
mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)].intervals;
if (intervals.size() < mFieldMatchers.size()) {
@@ -960,6 +1000,17 @@
interval.sampleSize += 1;
}
+ // State change.
+ if (!mSlicedStateAtoms.empty() && stateChange) {
+ // Turn OFF the condition timer for the previous state key.
+ mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)]
+ .conditionTimer.onConditionChanged(false, eventTimeNs);
+
+ // Turn ON the condition timer for the new state key.
+ mCurrentSlicedBucket[MetricDimensionKey(whatKey, stateKey)]
+ .conditionTimer.onConditionChanged(true, eventTimeNs);
+ }
+
// Only trigger the tracker if all intervals are correct and we have not skipped the bucket due
// to MULTIPLE_BUCKETS_SKIPPED.
if (useAnomalyDetection && !multipleBucketsSkipped(calcBucketsForwardCount(eventTimeNs))) {
@@ -1034,12 +1085,18 @@
if (!mCurrentBucketIsSkipped) {
bool bucketHasData = false;
// The current bucket is large enough to keep.
- for (const auto& slice : mCurrentSlicedBucket) {
- PastValueBucket bucket = buildPartialBucket(bucketEndTime, slice.second.intervals);
- bucket.mConditionTrueNs = conditionTrueDuration;
+ for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) {
+ PastValueBucket bucket =
+ buildPartialBucket(bucketEndTime, currentValueBucket.intervals);
+ if (!mSlicedStateAtoms.empty()) {
+ bucket.mConditionTrueNs =
+ currentValueBucket.conditionTimer.newBucketStart(bucketEndTime);
+ } else {
+ bucket.mConditionTrueNs = conditionTrueDuration;
+ }
// it will auto create new vector of ValuebucketInfo if the key is not found.
if (bucket.valueIndex.size() > 0) {
- auto& bucketList = mPastBuckets[slice.first];
+ auto& bucketList = mPastBuckets[metricDimensionKey];
bucketList.push_back(bucket);
bucketHasData = true;
}
@@ -1067,11 +1124,18 @@
buildDropEvent(eventTimeNs, BucketDropReason::NO_DATA));
mSkippedBuckets.emplace_back(bucketInGap);
}
-
appendToFullBucket(eventTimeNs > fullBucketEndTimeNs);
initCurrentSlicedBucket(nextBucketStartTimeNs);
// Update the condition timer again, in case we skipped buckets.
mConditionTimer.newBucketStart(nextBucketStartTimeNs);
+
+ // NOTE: Update the condition timers in `mCurrentSlicedBucket` only when slicing
+ // by state. Otherwise, the "global" condition timer will be used.
+ if (!mSlicedStateAtoms.empty()) {
+ for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) {
+ currentValueBucket.conditionTimer.newBucketStart(nextBucketStartTimeNs);
+ }
+ }
mCurrentBucketNum += numBucketsForward;
}
@@ -1113,6 +1177,17 @@
interval.seenNewData = false;
}
+ if (obsolete && !mSlicedStateAtoms.empty()) {
+ // When slicing by state, only delete the MetricDimensionKey when the
+ // state key in the MetricDimensionKey is not the current state key.
+ const HashableDimensionKey& dimensionInWhatKey = it->first.getDimensionKeyInWhat();
+ const auto& currentBaseInfoItr = mCurrentBaseInfo.find(dimensionInWhatKey);
+
+ if ((currentBaseInfoItr != mCurrentBaseInfo.end()) &&
+ (it->first.getStateValuesKey() == currentBaseInfoItr->second.currentState)) {
+ obsolete = false;
+ }
+ }
if (obsolete) {
it = mCurrentSlicedBucket.erase(it);
} else {
@@ -1148,7 +1223,7 @@
// Accumulate partial buckets with current value and then send to anomaly tracker.
if (mCurrentFullBucket.size() > 0) {
for (const auto& slice : mCurrentSlicedBucket) {
- if (hitFullBucketGuardRailLocked(slice.first)) {
+ if (hitFullBucketGuardRailLocked(slice.first) || slice.second.intervals.empty()) {
continue;
}
// TODO: fix this when anomaly can accept double values
@@ -1169,7 +1244,7 @@
// Skip aggregating the partial buckets since there's no previous partial bucket.
for (const auto& slice : mCurrentSlicedBucket) {
for (auto& tracker : mAnomalyTrackers) {
- if (tracker != nullptr) {
+ if (tracker != nullptr && !slice.second.intervals.empty()) {
// TODO: fix this when anomaly can accept double values
auto& interval = slice.second.intervals[0];
if (interval.hasValue) {
@@ -1183,10 +1258,12 @@
} else {
// Accumulate partial bucket.
for (const auto& slice : mCurrentSlicedBucket) {
- // TODO: fix this when anomaly can accept double values
- auto& interval = slice.second.intervals[0];
- if (interval.hasValue) {
- mCurrentFullBucket[slice.first] += interval.value.long_value;
+ if (!slice.second.intervals.empty()) {
+ // TODO: fix this when anomaly can accept double values
+ auto& interval = slice.second.intervals[0];
+ if (interval.hasValue) {
+ mCurrentFullBucket[slice.first] += interval.value.long_value;
+ }
}
}
}
diff --git a/bin/src/metrics/ValueMetricProducer.h b/bin/src/metrics/ValueMetricProducer.h
index 5f472d4..1de0524 100644
--- a/bin/src/metrics/ValueMetricProducer.h
+++ b/bin/src/metrics/ValueMetricProducer.h
@@ -209,8 +209,14 @@
// Internal state of an ongoing aggregation bucket.
typedef struct CurrentValueBucket {
+ // If the `MetricDimensionKey` state key is the current state key, then
+ // the condition timer will be updated later (e.g. condition/state/active
+ // state change) with the correct condition and time.
+ CurrentValueBucket() : intervals(), conditionTimer(ConditionTimer(false, 0)) {}
// Value information for each value field of the metric.
std::vector<Interval> intervals;
+ // Tracks how long the condition is true.
+ ConditionTimer conditionTimer;
} CurrentValueBucket;
// Holds base information for diffing values from one value field.
@@ -222,7 +228,10 @@
} BaseInfo;
// State key and base information for a specific DimensionsInWhat key.
- typedef struct {
+ typedef struct DimensionsInWhatInfo {
+ DimensionsInWhatInfo(const HashableDimensionKey& stateKey)
+ : baseInfos(), currentState(stateKey), hasCurrentState(false) {
+ }
std::vector<BaseInfo> baseInfos;
// Last seen state value(s).
HashableDimensionKey currentState;
@@ -268,6 +277,10 @@
// Reset diff base and mHasGlobalBase
void resetBase();
+ // Updates the condition timers in the current sliced bucket when there is a
+ // condition change or an active state change.
+ void updateCurrentSlicedBucketConditionTimers(bool newCondition, int64_t eventTimeNs);
+
static const size_t kBucketSize = sizeof(PastValueBucket{});
const size_t mDimensionSoftLimit;
@@ -353,6 +366,11 @@
FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey);
FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase);
FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMultipleDimensions);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataInStateChange);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithDataMissingInConditionChange);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataThenFlushBucket);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithNoPullOnBucketBoundary);
FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed);
FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed);
diff --git a/bin/tests/condition/ConditionTimer_test.cpp b/bin/tests/condition/ConditionTimer_test.cpp
index ea02cd3..46dc9a9 100644
--- a/bin/tests/condition/ConditionTimer_test.cpp
+++ b/bin/tests/condition/ConditionTimer_test.cpp
@@ -35,11 +35,11 @@
EXPECT_EQ(0, timer.mTimerNs);
timer.onConditionChanged(true, ct_start_time + 5);
- EXPECT_EQ(ct_start_time + 5, timer.mLastConditionTrueTimestampNs);
+ EXPECT_EQ(ct_start_time + 5, timer.mLastConditionChangeTimestampNs);
EXPECT_EQ(true, timer.mCondition);
EXPECT_EQ(95, timer.newBucketStart(ct_start_time + 100));
- EXPECT_EQ(ct_start_time + 100, timer.mLastConditionTrueTimestampNs);
+ EXPECT_EQ(ct_start_time + 100, timer.mLastConditionChangeTimestampNs);
EXPECT_EQ(true, timer.mCondition);
}
@@ -51,7 +51,7 @@
EXPECT_EQ(ct_start_time - time_base, timer.newBucketStart(ct_start_time));
EXPECT_EQ(true, timer.mCondition);
EXPECT_EQ(0, timer.mTimerNs);
- EXPECT_EQ(ct_start_time, timer.mLastConditionTrueTimestampNs);
+ EXPECT_EQ(ct_start_time, timer.mLastConditionChangeTimestampNs);
timer.onConditionChanged(false, ct_start_time + 5);
EXPECT_EQ(5, timer.mTimerNs);
diff --git a/bin/tests/metrics/ValueMetricProducer_test.cpp b/bin/tests/metrics/ValueMetricProducer_test.cpp
index b166cc1..6cf4192 100644
--- a/bin/tests/metrics/ValueMetricProducer_test.cpp
+++ b/bin/tests/metrics/ValueMetricProducer_test.cpp
@@ -93,6 +93,13 @@
}
}
+static void assertConditionTimer(const ConditionTimer& conditionTimer, bool condition,
+ int64_t timerNs, int64_t lastConditionTrueTimestampNs) {
+ EXPECT_EQ(condition, conditionTimer.mCondition);
+ EXPECT_EQ(timerNs, conditionTimer.mTimerNs);
+ EXPECT_EQ(lastConditionTrueTimestampNs, conditionTimer.mLastConditionChangeTimestampNs);
+}
+
} // anonymous namespace
class ValueMetricProducerTestHelper {
@@ -3967,33 +3974,37 @@
// Screen state change to ON.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5, 5));
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5 * NS_PER_SEC, 5));
return true;
}))
// Screen state change to OFF.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 9));
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 9));
return true;
}))
// Screen state change to ON.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 15, 21));
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 15 * NS_PER_SEC, 21));
return true;
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 30));
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 30));
return true;
}));
@@ -4025,12 +4036,13 @@
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
// Bucket status after screen state change kStateUnknown->ON.
auto screenEvent = CreateScreenStateChangedEvent(
- bucketStartTimeNs + 5, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+ bucketStartTimeNs + 5 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4040,34 +4052,13 @@
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for dimension, state key {{}, kStateUnknown}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second.intervals[0].hasValue);
- EXPECT_EQ(2, it->second.intervals[0].value.long_value);
-
- // Bucket status after screen state change ON->OFF.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10,
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
- StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {}
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
- itBase->second.currentState.getValues()[0].mValue.int_value);
// Value for dimension, state key {{}, ON}
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second.intervals[0].hasValue);
- EXPECT_EQ(4, it->second.intervals[0].value.long_value);
+ EXPECT_EQ(0, it->second.intervals.size());
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
// Value for dimension, state key {{}, kStateUnknown}
it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
@@ -4076,9 +4067,52 @@
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
+
+ // Bucket status after screen state change ON->OFF.
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
+ StateManager::getInstance().onLogEvent(*screenEvent);
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {}
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, OFF}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_EQ(0, it->second.intervals.size());
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+ // Value for dimension, state key {{}, ON}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_TRUE(it->second.intervals[0].hasValue);
+ EXPECT_EQ(4, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 10 * NS_PER_SEC);
+ // Value for dimension, state key {{}, kStateUnknown}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_TRUE(it->second.intervals[0].hasValue);
+ EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
// Bucket status after screen state change OFF->ON.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15,
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15 * NS_PER_SEC,
android::view::DisplayStateEnum::DISPLAY_STATE_ON);
StateManager::getInstance().onLogEvent(*screenEvent);
ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
@@ -4098,6 +4132,8 @@
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(12, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 15 * NS_PER_SEC);
// Value for dimension, state key {{}, ON}
it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
@@ -4106,6 +4142,8 @@
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(4, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, true, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 15 * NS_PER_SEC);
// Value for dimension, state key {{}, kStateUnknown}
it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
@@ -4114,37 +4152,46 @@
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
// Start dump report and check output.
ProtoOutputStream output;
std::set<string> strSet;
- valueProducer->onDumpReport(bucketStartTimeNs + 50, true /* include recent buckets */, true,
- NO_TIME_CONSTRAINTS, &strSet, &output);
+ valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
StatsLogReport report = outputStreamToProto(&output);
EXPECT_TRUE(report.has_value_metrics());
ASSERT_EQ(3, report.value_metrics().data_size());
+ // {{}, kStateUnknown}
auto data = report.value_metrics().data(0);
ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value());
+ EXPECT_EQ(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {{}, ON}
data = report.value_metrics().data(1);
ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size());
EXPECT_EQ(13, report.value_metrics().data(1).bucket_info(0).values(0).value_long());
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value());
+ EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {{}, OFF}
data = report.value_metrics().data(2);
ASSERT_EQ(1, report.value_metrics().data(2).bucket_info_size());
EXPECT_EQ(12, report.value_metrics().data(2).bucket_info(0).values(0).value_long());
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value());
+ EXPECT_EQ(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
}
/*
@@ -4169,9 +4216,10 @@
// Screen state change to ON.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5, 5));
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5 * NS_PER_SEC, 5));
return true;
}))
// Screen state change to VR has no pull because it is in the same
@@ -4183,17 +4231,19 @@
// Screen state change to OFF.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 15, 21));
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 15 * NS_PER_SEC, 21));
return true;
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 30));
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 30));
return true;
}));
@@ -4236,12 +4286,13 @@
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
// Bucket status after screen state change kStateUnknown->ON.
auto screenEvent = CreateScreenStateChangedEvent(
- bucketStartTimeNs + 5, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+ bucketStartTimeNs + 5 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4251,20 +4302,29 @@
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(screenOnGroup.group_id(),
itBase->second.currentState.getValues()[0].mValue.long_value);
+ // Value for dimension, state key {{}, ON GROUP}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(screenOnGroup.group_id(),
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
// Value for dimension, state key {{}, kStateUnknown}
+ it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
// Bucket status after screen state change ON->VR.
// Both ON and VR are in the same state group, so the base should not change.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10,
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC,
android::view::DisplayStateEnum::DISPLAY_STATE_VR);
StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4274,20 +4334,29 @@
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(screenOnGroup.group_id(),
itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, ON GROUP}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(screenOnGroup.group_id(),
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
// Value for dimension, state key {{}, kStateUnknown}
+ it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
// Bucket status after screen state change VR->ON.
// Both ON and VR are in the same state group, so the base should not change.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 12,
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 12 * NS_PER_SEC,
android::view::DisplayStateEnum::DISPLAY_STATE_ON);
StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4297,19 +4366,28 @@
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(screenOnGroup.group_id(),
itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, ON GROUP}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(screenOnGroup.group_id(),
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
// Value for dimension, state key {{}, kStateUnknown}
+ it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
// Bucket status after screen state change VR->OFF.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15,
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15 * NS_PER_SEC,
android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4319,13 +4397,22 @@
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(screenOffGroup.group_id(),
itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, OFF GROUP}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(screenOffGroup.group_id(),
+ it->first.getStateValuesKey().getValues()[0].mValue.long_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 15 * NS_PER_SEC);
// Value for dimension, state key {{}, ON GROUP}
+ it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(screenOnGroup.group_id(),
it->first.getStateValuesKey().getValues()[0].mValue.long_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(16, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 15 * NS_PER_SEC);
// Value for dimension, state key {{}, kStateUnknown}
it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
@@ -4334,37 +4421,46 @@
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
// Start dump report and check output.
ProtoOutputStream output;
std::set<string> strSet;
- valueProducer->onDumpReport(bucketStartTimeNs + 50, true /* include recent buckets */, true,
- NO_TIME_CONSTRAINTS, &strSet, &output);
+ valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
StatsLogReport report = outputStreamToProto(&output);
EXPECT_TRUE(report.has_value_metrics());
ASSERT_EQ(3, report.value_metrics().data_size());
+ // {{}, kStateUnknown}
auto data = report.value_metrics().data(0);
ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value());
+ EXPECT_EQ(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {{}, ON GROUP}
data = report.value_metrics().data(1);
ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size());
EXPECT_EQ(16, report.value_metrics().data(1).bucket_info(0).values(0).value_long());
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_group_id());
EXPECT_EQ(screenOnGroup.group_id(), data.slice_by_state(0).group_id());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {{}, OFF GROUP}
data = report.value_metrics().data(2);
ASSERT_EQ(1, report.value_metrics().data(2).bucket_info_size());
EXPECT_EQ(9, report.value_metrics().data(2).bucket_info(0).values(0).value_long());
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_group_id());
EXPECT_EQ(screenOffGroup.group_id(), data.slice_by_state(0).group_id());
+ EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
}
/*
@@ -4386,6 +4482,35 @@
auto fieldsInState = stateLink->mutable_fields_in_state();
*fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
+ /*
+ NOTE: "1" denotes uid 1 and "2" denotes uid 2.
+ bucket # 1 bucket # 2
+ 10 20 30 40 50 60 70 80 90 100 110 120 (seconds)
+ |------------------------------------------|---------------------------------|--
+
+ (kStateUnknown)
+ 1
+ |-------------|
+ 20
+
+ 2
+ |----------------------------|
+ 40
+
+ (FOREGROUND)
+ 1 1
+ |----------------------------|-------------| |------|
+ 40 20 10
+
+
+ (BACKGROUND)
+ 1
+ |------------|
+ 20
+ 2
+ |-------------|---------------------------------|
+ 20 50
+ */
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// ValueMetricProducer initialized.
@@ -4400,64 +4525,64 @@
// Uid 1 process state change from kStateUnknown -> Foreground
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
data->clear();
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20, 1 /*uid*/, 6));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+ 1 /*uid*/, 6));
// This event should be skipped.
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20, 2 /*uid*/, 8));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+ 2 /*uid*/, 8));
return true;
}))
// Uid 2 process state change from kStateUnknown -> Background
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
data->clear();
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40, 2 /*uid*/, 9));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+ 2 /*uid*/, 9));
// This event should be skipped.
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40, 1 /*uid*/, 12));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+ 1 /*uid*/, 12));
return true;
}))
// Uid 1 process state change from Foreground -> Background
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 20);
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 20 * NS_PER_SEC);
data->clear();
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20, 1 /*uid*/, 13));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20 * NS_PER_SEC,
+ 1 /*uid*/, 13));
// This event should be skipped.
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20, 2 /*uid*/, 11));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20 * NS_PER_SEC,
+ 2 /*uid*/, 11));
return true;
}))
// Uid 1 process state change from Background -> Foreground
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 40);
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 40 * NS_PER_SEC);
data->clear();
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40, 1 /*uid*/, 17));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40 * NS_PER_SEC,
+ 1 /*uid*/, 17));
// This event should be skipped.
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40, 2 /*uid */, 15));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40 * NS_PER_SEC,
+ 2 /*uid */, 15));
return true;
}))
// Dump report pull.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50);
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC);
data->clear();
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50, 2 /*uid*/, 20));
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50, 1 /*uid*/, 21));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50 * NS_PER_SEC,
+ 2 /*uid*/, 20));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50 * NS_PER_SEC,
+ 1 /*uid*/, 21));
return true;
}));
@@ -4489,6 +4614,7 @@
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
// Base for dimension key {uid 2}
it++;
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4505,12 +4631,14 @@
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
// Bucket status after uid 1 process state change kStateUnknown -> Foreground.
- auto uidProcessEvent = CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 20, 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
+ auto uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
StateManager::getInstance().onLogEvent(*uidProcessEvent);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
// Base for dimension key {uid 1}.
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4528,8 +4656,18 @@
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(3, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+ // Value for key {uid 1, FOREGROUND}.
+ it++;
+ ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
- // Base for dimension key {uid 2}
+ // Base for dimension key {uid 2}.
it++;
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
@@ -4538,22 +4676,42 @@
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {uid 2, kStateUnknown}
+ // Value for key {uid 2, kStateUnknown}.
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
// Bucket status after uid 2 process state change kStateUnknown -> Background.
- uidProcessEvent = CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 40, 2 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucketStartTimeNs + 40 * NS_PER_SEC, 2 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
StateManager::getInstance().onLogEvent(*uidProcessEvent);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {uid 1}.
+ ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {uid 2}.
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {uid 2, BACKGROUND}.
+ ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 40 * NS_PER_SEC);
+
+ // Base for dimension key {uid 1}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
EXPECT_EQ(6, itBase->second.baseInfos[0].base.long_value);
EXPECT_TRUE(itBase->second.hasCurrentState);
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
@@ -4563,26 +4721,33 @@
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(3, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
- // Base for dimension key {uid 2}
+ // Value for key {uid 1, FOREGROUND}.
it++;
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
// Value for key {uid 2, kStateUnknown}
+ it++;
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 40 * NS_PER_SEC,
+ bucketStartTimeNs + 40 * NS_PER_SEC);
// Pull at end of first bucket.
vector<shared_ptr<LogEvent>> allData;
@@ -4612,6 +4777,8 @@
EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+ EXPECT_EQ(20 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
// Base for dimension key {uid 1}
it++;
@@ -4629,6 +4796,8 @@
EXPECT_EQ(-1 /* kStateTracker::kUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+ EXPECT_EQ(20 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
// Value for key {uid 1, FOREGROUND}
it++;
@@ -4638,6 +4807,8 @@
EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+ EXPECT_EQ(40 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
// Value for key {uid 2, kStateUnknown}
it++;
@@ -4647,13 +4818,16 @@
EXPECT_EQ(-1 /* kStateTracker::kUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 40 * NS_PER_SEC);
+ EXPECT_EQ(40 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
// Bucket status after uid 1 process state change from Foreground -> Background.
- uidProcessEvent = CreateUidProcessStateChangedEvent(
- bucket2StartTimeNs + 20, 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 20 * NS_PER_SEC, 1 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
StateManager::getInstance().onLogEvent(*uidProcessEvent);
- ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size());
ASSERT_EQ(4UL, valueProducer->mPastBuckets.size());
ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
// Base for dimension key {uid 2}.
@@ -4672,6 +4846,8 @@
EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+
// Base for dimension key {uid 1}
it++;
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4688,6 +4864,17 @@
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {uid 1, BACKGROUND}
+ it++;
+ ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 20 * NS_PER_SEC);
+
// Value for key {uid 1, FOREGROUND}
it++;
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
@@ -4697,6 +4884,9 @@
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(3, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucket2StartTimeNs + 20 * NS_PER_SEC);
+
// Value for key {uid 2, kStateUnknown}
it++;
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
@@ -4705,10 +4895,12 @@
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 40 * NS_PER_SEC);
// Bucket status after uid 1 process state change Background->Foreground.
- uidProcessEvent = CreateUidProcessStateChangedEvent(
- bucket2StartTimeNs + 40, 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 40 * NS_PER_SEC, 1 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
StateManager::getInstance().onLogEvent(*uidProcessEvent);
ASSERT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size());
@@ -4729,6 +4921,7 @@
EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
// Base for dimension key {uid 1}
it++;
@@ -4746,6 +4939,7 @@
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
// Value for key {uid 1, BACKGROUND}
it++;
@@ -4756,6 +4950,8 @@
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(4, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucket2StartTimeNs + 40 * NS_PER_SEC);
// Value for key {uid 1, FOREGROUND}
it++;
@@ -4766,6 +4962,8 @@
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(3, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
+ bucket2StartTimeNs + 40 * NS_PER_SEC);
// Value for key {uid 2, kStateUnknown}
it++;
@@ -4774,17 +4972,20 @@
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 40 * NS_PER_SEC);
// Start dump report and check output.
ProtoOutputStream output;
std::set<string> strSet;
- valueProducer->onDumpReport(bucket2StartTimeNs + 50, true /* include recent buckets */, true,
- NO_TIME_CONSTRAINTS, &strSet, &output);
+ valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
StatsLogReport report = outputStreamToProto(&output);
EXPECT_TRUE(report.has_value_metrics());
ASSERT_EQ(5, report.value_metrics().data_size());
+ // {uid 1, BACKGROUND}
auto data = report.value_metrics().data(0);
ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(4, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
@@ -4792,14 +4993,18 @@
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
data.slice_by_state(0).value());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {uid 2, kStateUnknown}
data = report.value_metrics().data(1);
ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size());
EXPECT_EQ(2, report.value_metrics().data(1).bucket_info(0).values(0).value_long());
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value());
+ EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {uid 1, FOREGROUND}
data = report.value_metrics().data(2);
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
@@ -4808,14 +5013,19 @@
ASSERT_EQ(2, report.value_metrics().data(2).bucket_info_size());
EXPECT_EQ(4, report.value_metrics().data(2).bucket_info(0).values(0).value_long());
EXPECT_EQ(7, report.value_metrics().data(2).bucket_info(1).values(0).value_long());
+ EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+ // {uid 1, kStateUnknown}
data = report.value_metrics().data(3);
ASSERT_EQ(1, report.value_metrics().data(3).bucket_info_size());
EXPECT_EQ(3, report.value_metrics().data(3).bucket_info(0).values(0).value_long());
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {uid 2, BACKGROUND}
data = report.value_metrics().data(4);
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
@@ -4824,6 +5034,1630 @@
ASSERT_EQ(2, report.value_metrics().data(4).bucket_info_size());
EXPECT_EQ(6, report.value_metrics().data(4).bucket_info(0).values(0).value_long());
EXPECT_EQ(5, report.value_metrics().data(4).bucket_info(1).values(0).value_long());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+}
+
+/*
+ * Test slicing condition_true_nanos by state for metric that slices by state when data is not
+ * present in pulled data during a state change.
+ */
+TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataInStateChange) {
+ // Set up ValueMetricProducer.
+ ValueMetric metric =
+ ValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE");
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ /*
+ NOTE: "-" means that the data was not present in the pulled data.
+
+ bucket # 1
+ 10 20 30 40 50 60 (seconds)
+ |-------------------------------------------------------|--
+ x (kStateUnknown)
+ |-----------|
+ 10
+
+ x x (ON)
+ |---------------------| |-----------|
+ 20 10
+
+ - (OFF)
+ */
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ // ValueMetricProducer initialized.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
+ return true;
+ }))
+ // Battery saver mode state changed to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 5));
+ return true;
+ }))
+ // Battery saver mode state changed to OFF but data for dimension key {} is not present
+ // in the pulled data.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC);
+ data->clear();
+ return true;
+ }))
+ // Battery saver mode state changed to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC, 7));
+ return true;
+ }))
+ // Dump report pull.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 15));
+ return true;
+ }));
+
+ StateManager::getInstance().clear();
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithState(
+ pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {});
+ EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
+
+ // Set up StateManager and check that StateTrackers are initialized.
+ StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
+ valueProducer);
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
+ util::BATTERY_SAVER_MODE_STATE_CHANGED));
+
+ // Bucket status after metric initialized.
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {}
+ auto it = valueProducer->mCurrentSlicedBucket.begin();
+ auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, kStateUnknown}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
+
+ // Bucket status after battery saver mode ON event.
+ unique_ptr<LogEvent> batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+ // Base for dimension key {}
+
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Bucket status after battery saver mode OFF event which is not present
+ // in the pulled data.
+ unique_ptr<LogEvent> batterySaverOffEvent =
+ CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 30 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOffEvent);
+
+ // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_FALSE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Bucket status after battery saver mode ON event.
+ batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 40 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+ // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 40 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Start dump report and check output.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ ASSERT_EQ(2, report.value_metrics().data_size());
+
+ // {{}, kStateUnknown}
+ ValueMetricData data = report.value_metrics().data(0);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+ // {{}, ON}
+ data = report.value_metrics().data(1);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+}
+
+/*
+ * Test for metric that slices by state when data is not present in pulled data
+ * during an event and then a flush occurs for the current bucket. With the new
+ * condition timer behavior, a "new" MetricDimensionKey is inserted into
+ * `mCurrentSlicedBucket` before intervals are closed/added to that new
+ * MetricDimensionKey.
+ */
+TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataThenFlushBucket) {
+ // Set up ValueMetricProducer.
+ ValueMetric metric =
+ ValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE");
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ /*
+ NOTE: "-" means that the data was not present in the pulled data.
+
+ bucket # 1
+ 10 20 30 40 50 60 (seconds)
+ |-------------------------------------------------------|--
+ - (kStateUnknown)
+
+ - (ON)
+ */
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ // ValueMetricProducer initialized but data for dimension key {} is not present
+ // in the pulled data..
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
+ data->clear();
+ return true;
+ }))
+ // Battery saver mode state changed to ON but data for dimension key {} is not present
+ // in the pulled data.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+ data->clear();
+ return true;
+ }))
+ // Dump report pull.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 15));
+ return true;
+ }));
+
+ StateManager::getInstance().clear();
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithState(
+ pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {});
+ EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
+
+ // Set up StateManager and check that StateTrackers are initialized.
+ StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
+ valueProducer);
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
+ util::BATTERY_SAVER_MODE_STATE_CHANGED));
+
+ // Bucket status after metric initialized.
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer->mCurrentBaseInfo.size());
+
+ // Bucket status after battery saver mode ON event which is not present
+ // in the pulled data.
+ unique_ptr<LogEvent> batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+ ASSERT_EQ(0UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+
+ // Start dump report and check output.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ ASSERT_EQ(0, report.value_metrics().data_size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+}
+
+TEST(ValueMetricProducerTest, TestSlicedStateWithNoPullOnBucketBoundary) {
+ // Set up ValueMetricProducer.
+ ValueMetric metric =
+ ValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE");
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ /*
+ bucket # 1 bucket # 2
+ 10 20 30 40 50 60 70 80 90 100 110 120 (seconds)
+ |------------------------------------|---------------------------|--
+ x (kStateUnknown)
+ |-----|
+ 10
+ x x (ON)
+ |-----| |-----------|
+ 10 20
+ x (OFF)
+ |------------------------|
+ 40
+ */
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ // ValueMetricProducer initialized.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
+ return true;
+ }))
+ // Battery saver mode state changed to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 5));
+ return true;
+ }))
+ // Battery saver mode state changed to OFF.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, 7));
+ return true;
+ }))
+ // Battery saver mode state changed to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 30 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 10));
+ return true;
+ }))
+ // Dump report pull.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 15));
+ return true;
+ }));
+
+ StateManager::getInstance().clear();
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithState(
+ pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {});
+ EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
+
+ // Set up StateManager and check that StateTrackers are initialized.
+ StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
+ valueProducer);
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
+ util::BATTERY_SAVER_MODE_STATE_CHANGED));
+
+ // Bucket status after metric initialized.
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ auto it = valueProducer->mCurrentSlicedBucket.begin();
+ auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, kStateUnknown}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
+
+ // Bucket status after battery saver mode ON event.
+ unique_ptr<LogEvent> batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Bucket status after battery saver mode OFF event.
+ unique_ptr<LogEvent> batterySaverOffEvent =
+ CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 20 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOffEvent);
+
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::OFF,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, OFF}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::OFF,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{}, ON}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Bucket status after battery saver mode ON event.
+ batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucket2StartTimeNs + 30 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, OFF}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::OFF,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 30 * NS_PER_SEC,
+ bucket2StartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{}, ON}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Start dump report and check output.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ ASSERT_EQ(3, report.value_metrics().data_size());
+
+ // {{}, kStateUnknown}
+ ValueMetricData data = report.value_metrics().data(0);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+ // {{}, ON}
+ data = report.value_metrics().data(1);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+
+ // {{}, OFF}
+ data = report.value_metrics().data(2);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(BatterySaverModeStateChanged::OFF, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+}
+
+/*
+ * Test slicing condition_true_nanos by state for metric that slices by state when data is not
+ * present in pulled data during a condition change.
+ */
+TEST(ValueMetricProducerTest, TestSlicedStateWithDataMissingInConditionChange) {
+ // Set up ValueMetricProducer.
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithConditionAndState(
+ "BATTERY_SAVER_MODE_STATE");
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ /*
+ NOTE: "-" means that the data was not present in the pulled data.
+
+ bucket # 1
+ 10 20 30 40 50 60 (seconds)
+ |-------------------------------------------------------|--
+
+ T F T (Condition)
+ x (ON)
+ |----------------------| -
+ 20
+ */
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ // Battery saver mode state changed to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 3));
+ return true;
+ }))
+ // Condition changed to false.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30 * NS_PER_SEC, 5));
+ return true;
+ }))
+ // Condition changed to true but data for dimension key {} is not present in the
+ // pulled data.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
+ data->clear();
+ return true;
+ }))
+ // Dump report pull.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 20));
+ return true;
+ }));
+
+ StateManager::getInstance().clear();
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithConditionAndState(
+ pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {},
+ ConditionState::kTrue);
+ EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
+
+ // Set up StateManager and check that StateTrackers are initialized.
+ StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
+ valueProducer);
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
+ util::BATTERY_SAVER_MODE_STATE_CHANGED));
+
+ // Bucket status after battery saver mode ON event.
+ unique_ptr<LogEvent> batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+ // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ auto it = valueProducer->mCurrentSlicedBucket.begin();
+ auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket status after condition change to false.
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 30 * NS_PER_SEC);
+ // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket status after condition change to true.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 40 * NS_PER_SEC);
+ // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_FALSE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Start dump report and check output.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ ASSERT_EQ(1, report.value_metrics().data_size());
+
+ // {{}, ON}
+ ValueMetricData data = report.value_metrics().data(0);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+}
+
+/*
+ * Test slicing condition_true_nanos by state for metric that slices by state with a primary field,
+ * condition, and has multiple dimensions.
+ */
+TEST(ValueMetricProducerTest, TestSlicedStateWithMultipleDimensions) {
+ // Set up ValueMetricProducer.
+ ValueMetric metric =
+ ValueMetricProducerTestHelper::createMetricWithConditionAndState("UID_PROCESS_STATE");
+ metric.mutable_dimensions_in_what()->set_field(tagId);
+ metric.mutable_dimensions_in_what()->add_child()->set_field(1);
+ metric.mutable_dimensions_in_what()->add_child()->set_field(3);
+
+ MetricStateLink* stateLink = metric.add_state_link();
+ stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
+ auto fieldsInWhat = stateLink->mutable_fields_in_what();
+ *fieldsInWhat = CreateDimensions(tagId, {1 /* uid */});
+ auto fieldsInState = stateLink->mutable_fields_in_state();
+ *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
+
+ /*
+ bucket # 1 bucket # 2
+ 10 20 30 40 50 60 70 80 90 100 110 120 (seconds)
+ |------------------------------------------|---------------------------------|--
+
+ T F T (Condition)
+ (FOREGROUND)
+ x {1, 14}
+ |------|
+ 10
+
+ x {1, 16}
+ |------|
+ 10
+ x {2, 8}
+ |-------------|
+ 20
+
+ (BACKGROUND)
+ x {1, 14}
+ |-------------| |----------|---------------------------------|
+ 20 15 50
+
+ x {1, 16}
+ |-------------| |----------|---------------------------------|
+ 20 15 50
+
+ x {2, 8}
+ |----------| |----------|-------------------|
+ 15 15 30
+ */
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ // Uid 1 process state change from kStateUnknown -> Foreground
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC,
+ 1 /*uid*/, 3, 14 /*tag*/));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC,
+ 1 /*uid*/, 3, 16 /*tag*/));
+
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC,
+ 2 /*uid*/, 5, 8 /*tag*/));
+ return true;
+ }))
+ // Uid 1 process state change from Foreground -> Background
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+ 1 /*uid*/, 5, 14 /*tag*/));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+ 1 /*uid*/, 5, 16 /*tag*/));
+
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+ 2 /*uid*/, 7, 8 /*tag*/));
+
+ return true;
+ }))
+ // Uid 2 process state change from kStateUnknown -> Background
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 25 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
+ 2 /*uid*/, 9, 8 /*tag*/));
+
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
+ 1 /*uid*/, 9, 14 /* tag */));
+
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
+ 1 /*uid*/, 9, 16 /* tag */));
+
+ return true;
+ }))
+ // Condition changed to false.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+ 1 /*uid*/, 11, 14 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+ 1 /*uid*/, 11, 16 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+ 2 /*uid*/, 11, 8 /*tag*/));
+
+ return true;
+ }))
+ // Condition changed to true.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 45 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC,
+ 1 /*uid*/, 13, 14 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC,
+ 1 /*uid*/, 13, 16 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC,
+ 2 /*uid*/, 13, 8 /*tag*/));
+ return true;
+ }))
+ // Uid 2 process state change from Background -> Foreground
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 30 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 2 /*uid*/, 18, 8 /*tag*/));
+
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, 18, 14 /* tag */));
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, 18, 16 /* tag */));
+
+ return true;
+ }))
+ // Dump report pull.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 1 /*uid*/, 21, 14 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 1 /*uid*/, 21, 16 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 2 /*uid*/, 21, 8 /*tag*/));
+ return true;
+ }));
+
+ StateManager::getInstance().clear();
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithConditionAndState(
+ pullerManager, metric, {UID_PROCESS_STATE_ATOM_ID}, {}, ConditionState::kTrue);
+ EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
+
+ // Set up StateManager and check that StateTrackers are initialized.
+ StateManager::getInstance().registerListener(UID_PROCESS_STATE_ATOM_ID, valueProducer);
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
+
+ // Condition is true.
+ // Bucket status after uid 1 process state change kStateUnknown -> Foreground.
+ auto uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC, 1 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
+ StateManager::getInstance().onLogEvent(*uidProcessEvent);
+ ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension {uid 1, tag 16}.
+ auto it = valueProducer->mCurrentSlicedBucket.begin();
+ auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket status after uid 1 process state change Foreground -> Background.
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
+ StateManager::getInstance().onLogEvent(*uidProcessEvent);
+ ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(6UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension {uid 1, tag 16}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket status after uid 2 process state change kStateUnknown -> Background.
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucketStartTimeNs + 25 * NS_PER_SEC, 2 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
+ StateManager::getInstance().onLogEvent(*uidProcessEvent);
+ ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension {uid 2, tag 8}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 2, uid 8}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 25 * NS_PER_SEC);
+
+ // Value for key {{uid 2, uid 8}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Base for dimension {uid 1, tag 16}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket 1 status after condition change to false.
+ // All condition timers should be turned off.
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 40 * NS_PER_SEC);
+ ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension {uid 2, tag 8}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 2, uid 8}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 15 * NS_PER_SEC,
+ bucketStartTimeNs + 40 * NS_PER_SEC);
+
+ // Value for key {{uid 2, uid 8}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Base for dimension {uid 1, tag 16}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 40 * NS_PER_SEC);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 40 * NS_PER_SEC);
+
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket 1 status after condition change to true.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 45 * NS_PER_SEC);
+ ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension {uid 2, tag 8}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 2, uid 8}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 15 * NS_PER_SEC,
+ bucketStartTimeNs + 45 * NS_PER_SEC);
+
+ // Value for key {{uid 2, uid 8}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Base for dimension {uid 1, tag 16}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 45 * NS_PER_SEC);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 45 * NS_PER_SEC);
+
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Pull at end of first bucket.
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(
+ CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 14 /* tag */));
+ allData.push_back(
+ CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 16 /* tag */));
+ allData.push_back(
+ CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 2 /*uid*/, 13, 8 /*tag*/));
+ valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
+
+ // Buckets flushed after end of first bucket.
+ // All condition timers' behavior should rollover to bucket 2.
+ ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(5UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+ // Base for dimension {uid 2, tag 8}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 2, uid 8}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+ ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+ EXPECT_EQ(30 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+ // Value for key {{uid 2, uid 8}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Base for dimension {uid 1, tag 16}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+ ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+ EXPECT_EQ(35 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+ ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+ EXPECT_EQ(35 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+ ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+ EXPECT_EQ(10 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+ ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+ EXPECT_EQ(10 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket 2 status after uid 2 process state change Background->Foreground.
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 30 * NS_PER_SEC, 2 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
+ StateManager::getInstance().onLogEvent(*uidProcessEvent);
+
+ ASSERT_EQ(9UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+ // Base for dimension {uid 2, tag 8}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 2, uid 8}, FOREGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{uid 2, uid 8}, BACKGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 30 * NS_PER_SEC,
+ bucket2StartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{uid 2, uid 8}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Base for dimension {uid 1, tag 16}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Start dump report and check output.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ ASSERT_EQ(6, report.value_metrics().data_size());
+
+ // {{uid 1, tag 14}, FOREGROUND}.
+ auto data = report.value_metrics().data(0);
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+ // {{uid 1, tag 16}, BACKGROUND}.
+ data = report.value_metrics().data(1);
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+
+ // {{uid 1, tag 14}, BACKGROUND}.
+ data = report.value_metrics().data(2);
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+
+ // {{uid 1, tag 16}, FOREGROUND}.
+ data = report.value_metrics().data(3);
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+ // {{uid 2, tag 8}, FOREGROUND}.
+ data = report.value_metrics().data(4);
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+ // {{uid 2, tag 8}, BACKGROUND}.
+ data = report.value_metrics().data(5);
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
}
TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) {
@@ -4894,15 +6728,23 @@
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(BatterySaverModeStateChanged::ON,
itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{}, -1}
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ // Value for key {{}, ON}
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
std::unordered_map<MetricDimensionKey, ValueMetricProducer::CurrentValueBucket>::iterator it =
valueProducer->mCurrentSlicedBucket.begin();
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
// Bucket status after battery saver mode OFF event.
unique_ptr<LogEvent> batterySaverOffEvent =
@@ -4917,15 +6759,27 @@
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(BatterySaverModeStateChanged::OFF,
itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{}, ON}
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ // Value for key {{}, OFF}
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
it = valueProducer->mCurrentSlicedBucket.begin();
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::OFF,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 30 * NS_PER_SEC);
+ // Value for key {{}, ON}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(BatterySaverModeStateChanged::ON,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 30 * NS_PER_SEC);
+ // Value for key {{}, -1}
+ it++;
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
// Pull at end of first bucket.
vector<shared_ptr<LogEvent>> allData;
@@ -4944,6 +6798,15 @@
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(BatterySaverModeStateChanged::OFF,
itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, OFF}
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+ // Value for key {{}, ON}
+ it++;
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 30 * NS_PER_SEC);
+ // Value for key {{}, -1}
+ it++;
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
// Bucket 2 status after condition change to false.
valueProducer->onConditionChanged(false, bucket2StartTimeNs + 10 * NS_PER_SEC);
@@ -4964,6 +6827,19 @@
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(4, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucket2StartTimeNs + 10 * NS_PER_SEC);
+ // Value for key {{}, ON}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 30 * NS_PER_SEC);
+ // Value for key {{}, -1}
+ it++;
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
// Start dump report and check output.
ProtoOutputStream output;
@@ -4982,6 +6858,7 @@
EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(2, data.bucket_info(0).values(0).value_long());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
data = report.value_metrics().data(1);
EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
@@ -4990,6 +6867,8 @@
ASSERT_EQ(2, data.bucket_info_size());
EXPECT_EQ(6, data.bucket_info(0).values(0).value_long());
EXPECT_EQ(4, data.bucket_info(1).values(0).value_long());
+ EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
}
/*