Add unit tests for CountMetricProducer, EventMetricProducer

And other miscellaneous fixes.
+ clang-format
+ 2 bug fixes, one in dump-report command, one in ResourcePowerManagerPuller

Test: statsd_test

Change-Id: Ibd164d948ad62adcc529d813df1210781e38be47
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 86f353b..87d318b 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -157,8 +157,10 @@
     tests/LogReader_test.cpp \
     tests/MetricsManager_test.cpp \
     tests/UidMap_test.cpp \
-    tests/OringDurationTracker_test.cpp \
-    tests/MaxDurationTracker_test.cpp
+    tests/metrics/OringDurationTracker_test.cpp \
+    tests/metrics/MaxDurationTracker_test.cpp \
+    tests/metrics/CountMetricProducer_test.cpp \
+    tests/metrics/EventMetricProducer_test.cpp
 
 
 LOCAL_STATIC_LIBRARIES := \
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index c0cedb1..8910523 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -89,7 +89,7 @@
         *dest = m;
     }
     auto temp = mUidMap->getOutput(key);
-    report.set_allocated_uid_map(&temp);
+    report.mutable_uid_map()->Swap(&temp);
     return report;
 }
 
diff --git a/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp b/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp
index 38953f1..3608ee4 100644
--- a/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp
+++ b/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp
@@ -83,28 +83,31 @@
         return false;
     }
 
+    uint64_t timestamp = time(nullptr) * NS_PER_SEC;
+
     data->clear();
     Return<void> ret = gPowerHalV1_0->getPlatformLowPowerStats(
-            [&data](hidl_vec<PowerStatePlatformSleepState> states, Status status) {
+            [&data, timestamp](hidl_vec<PowerStatePlatformSleepState> states, Status status) {
 
                 if (status != Status::SUCCESS) return;
 
                 for (size_t i = 0; i < states.size(); i++) {
                     const PowerStatePlatformSleepState& state = states[i];
 
-                    auto statePtr = make_shared<LogEvent>(power_state_platform_sleep_state_tag);
+                    auto statePtr =
+                            make_shared<LogEvent>(power_state_platform_sleep_state_tag, timestamp);
                     auto elemList = statePtr->GetAndroidLogEventList();
                     *elemList << state.name;
                     *elemList << state.residencyInMsecSinceBoot;
                     *elemList << state.totalTransitions;
                     *elemList << state.supportedOnlyInSuspend;
+                    statePtr->init();
                     data->push_back(statePtr);
-
                     VLOG("powerstate: %s, %lld, %lld, %d", state.name.c_str(),
                          (long long)state.residencyInMsecSinceBoot,
                          (long long)state.totalTransitions, state.supportedOnlyInSuspend ? 1 : 0);
                     for (auto voter : state.voters) {
-                        auto voterPtr = make_shared<LogEvent>(power_state_voter_tag);
+                        auto voterPtr = make_shared<LogEvent>(power_state_voter_tag, timestamp);
                         auto elemList = voterPtr->GetAndroidLogEventList();
                         *elemList << state.name;
                         *elemList << voter.name;
@@ -128,7 +131,7 @@
             android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
     if (gPowerHal_1_1 != nullptr) {
         ret = gPowerHal_1_1->getSubsystemLowPowerStats(
-                [&data](hidl_vec<PowerStateSubsystem> subsystems, Status status) {
+                [&data, timestamp](hidl_vec<PowerStateSubsystem> subsystems, Status status) {
 
                     if (status != Status::SUCCESS) return;
 
@@ -137,8 +140,8 @@
                             const PowerStateSubsystem& subsystem = subsystems[i];
                             for (size_t j = 0; j < subsystem.states.size(); j++) {
                                 const PowerStateSubsystemSleepState& state = subsystem.states[j];
-                                auto subsystemStatePtr =
-                                        make_shared<LogEvent>(power_state_subsystem_state_tag);
+                                auto subsystemStatePtr = make_shared<LogEvent>(
+                                        power_state_subsystem_state_tag, timestamp);
                                 auto elemList = subsystemStatePtr->GetAndroidLogEventList();
                                 *elemList << subsystem.name;
                                 *elemList << state.name;
@@ -146,6 +149,7 @@
                                 *elemList << state.totalTransitions;
                                 *elemList << state.lastEntryTimestampMs;
                                 *elemList << state.supportedOnlyInSuspend;
+                                subsystemStatePtr->init();
                                 data->push_back(subsystemStatePtr);
                                 VLOG("subsystemstate: %s, %s, %lld, %lld, %lld",
                                      subsystem.name.c_str(), state.name.c_str(),
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 003b5c4..43543cc 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -30,11 +30,11 @@
 
 #include <iostream>
 
+using std::make_shared;
 using std::map;
+using std::shared_ptr;
 using std::string;
 using std::vector;
-using std::make_shared;
-using std::shared_ptr;
 
 namespace android {
 namespace os {
@@ -42,40 +42,35 @@
 
 StatsPullerManager::StatsPullerManager()
     : mCurrentPullingInterval(LONG_MAX), mPullStartTimeMs(get_pull_start_time_ms()) {
-    shared_ptr<StatsPuller> statsCompanionServicePuller = make_shared<StatsCompanionServicePuller>();
-    shared_ptr <StatsPuller>
-        resourcePowerManagerPuller = make_shared<ResourcePowerManagerPuller>();
+    shared_ptr<StatsPuller> statsCompanionServicePuller =
+            make_shared<StatsCompanionServicePuller>();
+    shared_ptr<StatsPuller> resourcePowerManagerPuller = make_shared<ResourcePowerManagerPuller>();
 
-    mPullers.insert({android::util::KERNEL_WAKELOCK_PULLED,
-                     statsCompanionServicePuller});
-    mPullers.insert({android::util::WIFI_BYTES_TRANSFERRED,
-                     statsCompanionServicePuller});
-    mPullers.insert({android::util::MOBILE_BYTES_TRANSFERRED,
-                     statsCompanionServicePuller});
-    mPullers.insert({android::util::WIFI_BYTES_TRANSFERRED_BY_FG_BG,
-                     statsCompanionServicePuller});
-    mPullers.insert({android::util::MOBILE_BYTES_TRANSFERRED_BY_FG_BG,
-                     statsCompanionServicePuller});
-    mPullers.insert({android::util::POWER_STATE_PLATFORM_SLEEP_STATE_PULLED,
-                     resourcePowerManagerPuller});
-    mPullers.insert({android::util::POWER_STATE_VOTER_PULLED,
-                     resourcePowerManagerPuller});
-    mPullers.insert({android::util::POWER_STATE_SUBSYSTEM_SLEEP_STATE_PULLED,
-                     resourcePowerManagerPuller});
+    mPullers.insert({android::util::KERNEL_WAKELOCK_PULLED, statsCompanionServicePuller});
+    mPullers.insert({android::util::WIFI_BYTES_TRANSFERRED, statsCompanionServicePuller});
+    mPullers.insert({android::util::MOBILE_BYTES_TRANSFERRED, statsCompanionServicePuller});
+    mPullers.insert({android::util::WIFI_BYTES_TRANSFERRED_BY_FG_BG, statsCompanionServicePuller});
+    mPullers.insert(
+            {android::util::MOBILE_BYTES_TRANSFERRED_BY_FG_BG, statsCompanionServicePuller});
+    mPullers.insert(
+            {android::util::POWER_STATE_PLATFORM_SLEEP_STATE_PULLED, resourcePowerManagerPuller});
+    mPullers.insert({android::util::POWER_STATE_VOTER_PULLED, resourcePowerManagerPuller});
+    mPullers.insert(
+            {android::util::POWER_STATE_SUBSYSTEM_SLEEP_STATE_PULLED, resourcePowerManagerPuller});
 
     mStatsCompanionService = StatsService::getStatsCompanionService();
 }
 
-        bool StatsPullerManager::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) {
-            if (DEBUG) ALOGD("Initiating pulling %d", tagId);
+bool StatsPullerManager::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) {
+    if (DEBUG) ALOGD("Initiating pulling %d", tagId);
 
-            if (mPullers.find(tagId) != mPullers.end()) {
-                return mPullers.find(tagId)->second->Pull(tagId, data);
-            } else {
-                ALOGD("Unknown tagId %d", tagId);
-                return false;  // Return early since we don't know what to pull.
-            }
-        }
+    if (mPullers.find(tagId) != mPullers.end()) {
+        return mPullers.find(tagId)->second->Pull(tagId, data);
+    } else {
+        ALOGD("Unknown tagId %d", tagId);
+        return false;  // Return early since we don't know what to pull.
+    }
+}
 
 StatsPullerManager& StatsPullerManager::GetInstance() {
     static StatsPullerManager instance;
@@ -91,7 +86,8 @@
     return time(nullptr) * 1000;
 }
 
-void StatsPullerManager::RegisterReceiver(int tagId, sp<PullDataReceiver> receiver, long intervalMs) {
+void StatsPullerManager::RegisterReceiver(int tagId, sp<PullDataReceiver> receiver,
+                                          long intervalMs) {
     AutoMutex _l(mReceiversLock);
     vector<ReceiverInfo>& receivers = mReceivers[tagId];
     for (auto it = receivers.begin(); it != receivers.end(); it++) {
@@ -143,8 +139,8 @@
             vector<pair<int, vector<ReceiverInfo*>>>();
     for (auto& pair : mReceivers) {
         vector<ReceiverInfo*> receivers = vector<ReceiverInfo*>();
-        if (pair.second.size() != 0){
-            for(auto& receiverInfo : pair.second) {
+        if (pair.second.size() != 0) {
+            for (auto& receiverInfo : pair.second) {
                 if (receiverInfo.timeInfo.first + receiverInfo.timeInfo.second > currentTimeMs) {
                     receivers.push_back(&receiverInfo);
                 }
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 8220fcb..913b906 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -35,7 +35,7 @@
     init(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec, &mList);
 }
 
-LogEvent::LogEvent(int tag) : mList(tag) {
+LogEvent::LogEvent(int tag, uint64_t timestampNs) : mList(tag), mTimestampNs(timestampNs) {
 }
 
 LogEvent::~LogEvent() {
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index df75d9f..2984940 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -50,7 +50,7 @@
      * any of the values. This constructor is useful for unit-testing since we can't pass in an
      * android_log_event_list since there is no copy constructor or assignment operator available.
      */
-    explicit LogEvent(int tag);
+    explicit LogEvent(int tag, uint64_t timestampNs);
 
     ~LogEvent();
 
@@ -123,7 +123,9 @@
     vector<android_log_list_element> mElements;
     // Need a copy of the android_log_event_list so the strings are not cleared.
     android_log_event_list mList;
-    long mTimestampNs;
+
+    uint64_t mTimestampNs;
+
     int mTagId;
 };
 
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 6f5db51..100a7a4 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -58,10 +58,9 @@
 
 // TODO: add back AnomalyTracker.
 CountMetricProducer::CountMetricProducer(const CountMetric& metric, const int conditionIndex,
-                                         const sp<ConditionWizard>& wizard)
-    // TODO: Pass in the start time from MetricsManager, instead of calling time() here.
-    : MetricProducer((time(nullptr) * NANO_SECONDS_IN_A_SECOND), conditionIndex, wizard),
-      mMetric(metric) {
+                                         const sp<ConditionWizard>& wizard,
+                                         const uint64_t startTimeNs)
+    : MetricProducer(startTimeNs, conditionIndex, wizard), mMetric(metric) {
     // TODO: evaluate initial conditions. and set mConditionMet.
     if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
         mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000;
@@ -114,15 +113,17 @@
 }
 
 StatsLogReport CountMetricProducer::onDumpReport() {
-    long long endTime = time(nullptr) * NANO_SECONDS_IN_A_SECOND;
+    long long endTime = time(nullptr) * NS_PER_SEC;
 
     // Dump current bucket if it's stale.
     // If current bucket is still on-going, don't force dump current bucket.
     // In finish(), We can force dump current bucket.
     flushCounterIfNeeded(endTime);
+    VLOG("metric %lld dump report now...", mMetric.metric_id());
 
-    for (const auto& counter : mPastBucketProtos) {
+    for (const auto& counter : mPastBuckets) {
         const HashableDimensionKey& hashableKey = counter.first;
+        VLOG("  dimension key %s", hashableKey.c_str());
         auto it = mDimensionKeyMap.find(hashableKey);
         if (it == mDimensionKeyMap.end()) {
             ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str());
@@ -147,20 +148,17 @@
         }
 
         // Then fill bucket_info (CountBucketInfo).
-        for (const auto& proto : counter.second) {
-            size_t bufferSize = proto->size();
-            char* buffer(new char[bufferSize]);
-            size_t pos = 0;
-            auto it = proto->data();
-            while (it.readBuffer() != NULL) {
-                size_t toRead = it.currentToRead();
-                std::memcpy(&buffer[pos], it.readBuffer(), toRead);
-                pos += toRead;
-                it.rp()->move(toRead);
-            }
-            mProto->write(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION, buffer, bufferSize);
+        for (const auto& bucket : counter.second) {
+            long long bucketInfoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_BUCKET_INFO);
+            mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
+                          (long long)bucket.mBucketStartNs);
+            mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
+                          (long long)bucket.mBucketEndNs);
+            mProto->write(FIELD_TYPE_INT64 | FIELD_ID_COUNT, (long long)bucket.mCount);
+            mProto->end(bucketInfoToken);
+            VLOG("\t bucket [%lld - %lld] count: %lld", (long long)bucket.mBucketStartNs,
+                 (long long)bucket.mBucketEndNs, (long long)bucket.mCount);
         }
-
         mProto->end(wrapperToken);
     }
 
@@ -169,8 +167,8 @@
                   (long long)mCurrentBucketStartTimeNs);
 
     size_t bufferSize = mProto->size();
-    VLOG("metric %lld dump report now...", mMetric.metric_id());
     std::unique_ptr<uint8_t[]> buffer(new uint8_t[bufferSize]);
+
     size_t pos = 0;
     auto it = mProto->data();
     while (it.readBuffer() != NULL) {
@@ -181,7 +179,7 @@
     }
 
     startNewProtoOutputStream(endTime);
-    mPastBucketProtos.clear();
+    mPastBuckets.clear();
     mByteSize = 0;
 
     // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this:
@@ -239,20 +237,16 @@
     // adjust the bucket start time
     int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
 
+    CountBucket info;
+    info.mBucketStartNs = mCurrentBucketStartTimeNs;
+    info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
     for (const auto& counter : mCurrentSlicedCounter) {
-        unique_ptr<ProtoOutputStream> proto = make_unique<ProtoOutputStream>();
-        proto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
-                     (long long)mCurrentBucketStartTimeNs);
-        proto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
-                      (long long)mCurrentBucketStartTimeNs + mBucketSizeNs);
-        proto->write(FIELD_TYPE_INT64 | FIELD_ID_COUNT, (long long)counter.second);
-
-        auto& bucketList = mPastBucketProtos[counter.first];
-        mByteSize += proto->size();
-        bucketList.push_back(std::move(proto));
-
+        info.mCount = counter.second;
+        auto& bucketList = mPastBuckets[counter.first];
+        bucketList.push_back(info);
         VLOG("metric %lld, dump key value: %s -> %d", mMetric.metric_id(), counter.first.c_str(),
              counter.second);
+        mByteSize += sizeof(info);
     }
 
     // TODO: Re-add anomaly detection (similar to):
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 473a4ba..3bfc724 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -20,6 +20,7 @@
 #include <unordered_map>
 
 #include <android/util/ProtoOutputStream.h>
+#include <gtest/gtest_prod.h>
 #include "../condition/ConditionTracker.h"
 #include "../matchers/matcher_util.h"
 #include "CountAnomalyTracker.h"
@@ -34,11 +35,17 @@
 namespace os {
 namespace statsd {
 
+struct CountBucket {
+    int64_t mBucketStartNs;
+    int64_t mBucketEndNs;
+    int64_t mCount;
+};
+
 class CountMetricProducer : public MetricProducer {
 public:
     // TODO: Pass in the start time from MetricsManager, it should be consistent for all metrics.
     CountMetricProducer(const CountMetric& countMetric, const int conditionIndex,
-                        const sp<ConditionWizard>& wizard);
+                        const sp<ConditionWizard>& wizard, const uint64_t startTimeNs);
 
     virtual ~CountMetricProducer();
 
@@ -66,8 +73,7 @@
 private:
     const CountMetric mMetric;
 
-    std::unordered_map<HashableDimensionKey,
-        std::vector<unique_ptr<android::util::ProtoOutputStream>>> mPastBucketProtos;
+    std::unordered_map<HashableDimensionKey, std::vector<CountBucket>> mPastBuckets;
 
     size_t mByteSize;
 
@@ -83,6 +89,10 @@
     long long mProtoToken;
 
     void startNewProtoOutputStream(long long timestamp);
+
+    FRIEND_TEST(CountMetricProducerTest, TestNonDimensionalEvents);
+    FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition);
+    FRIEND_TEST(CountMetricProducerTest, TestEventsWithSlicedCondition);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 340f503..09132bf 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -35,9 +35,9 @@
                                                const int conditionIndex, const size_t startIndex,
                                                const size_t stopIndex, const size_t stopAllIndex,
                                                const sp<ConditionWizard>& wizard,
-                                               const vector<KeyMatcher>& internalDimension)
-    // TODO: Pass in the start time from MetricsManager, instead of calling time() here.
-    : MetricProducer(time(nullptr) * NANO_SECONDS_IN_A_SECOND, conditionIndex, wizard),
+                                               const vector<KeyMatcher>& internalDimension,
+                                               const uint64_t startTimeNs)
+    : MetricProducer(startTimeNs, conditionIndex, wizard),
       mMetric(metric),
       mStartIndex(startIndex),
       mStopIndex(stopIndex),
@@ -131,7 +131,7 @@
     // Dump current bucket if it's stale.
     // If current bucket is still on-going, don't force dump current bucket.
     // In finish(), We can force dump current bucket.
-    flushIfNeeded(time(nullptr) * NANO_SECONDS_IN_A_SECOND);
+    flushIfNeeded(time(nullptr) * NS_PER_SEC);
     report.set_end_report_nanos(mCurrentBucketStartTimeNs);
 
     StatsLogReport_DurationMetricDataWrapper* wrapper = report.mutable_duration_metrics();
@@ -195,10 +195,10 @@
 }
 
 size_t DurationMetricProducer::byteSize() {
-// TODO: return actual proto size when ProtoOutputStream is ready for use for
-// DurationMetricsProducer.
-//    return mProto->size();
-  return 0;
+    // TODO: return actual proto size when ProtoOutputStream is ready for use for
+    // DurationMetricsProducer.
+    //    return mProto->size();
+    return 0;
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index febf25d..12ff58e 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -40,7 +40,7 @@
     DurationMetricProducer(const DurationMetric& durationMetric, const int conditionIndex,
                            const size_t startIndex, const size_t stopIndex,
                            const size_t stopAllIndex, const sp<ConditionWizard>& wizard,
-                           const vector<KeyMatcher>& internalDimension);
+                           const vector<KeyMatcher>& internalDimension, const uint64_t startTimeNs);
 
     virtual ~DurationMetricProducer();
 
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index cbae1d3..677ae38 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -46,10 +46,9 @@
 const int FIELD_ID_STATS_EVENTS = 2;
 
 EventMetricProducer::EventMetricProducer(const EventMetric& metric, const int conditionIndex,
-                                         const sp<ConditionWizard>& wizard)
-    // TODO: Pass in the start time from MetricsManager, instead of calling time() here.
-    : MetricProducer((time(nullptr) * NANO_SECONDS_IN_A_SECOND), conditionIndex, wizard),
-      mMetric(metric) {
+                                         const sp<ConditionWizard>& wizard,
+                                         const uint64_t startTimeNs)
+    : MetricProducer(startTimeNs, conditionIndex, wizard), mMetric(metric) {
     if (metric.links().size() > 0) {
         mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
                                metric.links().end());
@@ -82,7 +81,7 @@
 }
 
 StatsLogReport EventMetricProducer::onDumpReport() {
-    long long endTime = time(nullptr) * NANO_SECONDS_IN_A_SECOND;
+    long long endTime = time(nullptr) * NS_PER_SEC;
     mProto->end(mProtoToken);
     mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, endTime);
 
@@ -114,7 +113,6 @@
         const size_t matcherIndex, const HashableDimensionKey& eventKey,
         const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
         const LogEvent& event, bool scheduledPull) {
-
     if (!condition) {
         return;
     }
@@ -128,7 +126,7 @@
 }
 
 size_t EventMetricProducer::byteSize() {
-  return mProto->size();
+    return mProto->size();
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 7dd0e38..0fc2b5b 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -20,6 +20,7 @@
 #include <unordered_map>
 
 #include <android/util/ProtoOutputStream.h>
+
 #include "../condition/ConditionTracker.h"
 #include "../matchers/matcher_util.h"
 #include "MetricProducer.h"
@@ -35,13 +36,14 @@
 public:
     // TODO: Pass in the start time from MetricsManager, it should be consistent for all metrics.
     EventMetricProducer(const EventMetric& eventMetric, const int conditionIndex,
-                        const sp<ConditionWizard>& wizard);
+                        const sp<ConditionWizard>& wizard, const uint64_t startTimeNs);
 
     virtual ~EventMetricProducer();
 
     void onMatchedLogEventInternal(const size_t matcherIndex, const HashableDimensionKey& eventKey,
                                    const std::map<std::string, HashableDimensionKey>& conditionKey,
-                                   bool condition, const LogEvent& event, bool scheduledPull) override;
+                                   bool condition, const LogEvent& event,
+                                   bool scheduledPull) override;
 
     void onConditionChanged(const bool conditionMet, const uint64_t eventTime) override;
 
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index bd638b4..285c8f4 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -35,7 +35,7 @@
 
 GaugeMetricProducer::GaugeMetricProducer(const GaugeMetric& metric, const int conditionIndex,
                                          const sp<ConditionWizard>& wizard, const int pullTagId)
-    : MetricProducer((time(nullptr) * NANO_SECONDS_IN_A_SECOND), conditionIndex, wizard),
+    : MetricProducer((time(nullptr) * NS_PER_SEC), conditionIndex, wizard),
       mMetric(metric),
       mPullTagId(pullTagId) {
     if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
@@ -94,7 +94,7 @@
     // Dump current bucket if it's stale.
     // If current bucket is still on-going, don't force dump current bucket.
     // In finish(), We can force dump current bucket.
-    flushGaugeIfNeededLocked(time(nullptr) * NANO_SECONDS_IN_A_SECOND);
+    flushGaugeIfNeededLocked(time(nullptr) * NS_PER_SEC);
     report.set_end_report_nanos(mCurrentBucketStartTimeNs);
 
     StatsLogReport_GaugeMetricDataWrapper* wrapper = report.mutable_gauge_metrics();
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 3b117ec..6ba726f4 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -40,6 +40,7 @@
         : mStartTimeNs(startTimeNs),
           mCurrentBucketStartTimeNs(startTimeNs),
           mCondition(conditionIndex >= 0 ? false : true),
+          mConditionSliced(false),
           mWizard(wizard),
           mConditionTrackerIndex(conditionIndex) {
         // reuse the same map for non-sliced metrics too. this way, we avoid too many if-else.
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 9b708dd6..80b325f 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -15,17 +15,17 @@
  */
 #define DEBUG true  // STOPSHIP if true
 #include "Log.h"
-
 #include "MetricsManager.h"
-#include <log/logprint.h>
-#include "../condition/CombinationConditionTracker.h"
-#include "../condition/SimpleConditionTracker.h"
-#include "../matchers/CombinationLogMatchingTracker.h"
-#include "../matchers/SimpleLogMatchingTracker.h"
+
 #include "CountMetricProducer.h"
+#include "condition/CombinationConditionTracker.h"
+#include "condition/SimpleConditionTracker.h"
+#include "matchers/CombinationLogMatchingTracker.h"
+#include "matchers/SimpleLogMatchingTracker.h"
 #include "metrics_manager_util.h"
 #include "stats_util.h"
 
+#include <log/logprint.h>
 using std::make_unique;
 using std::set;
 using std::string;
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 44cd637..63e2c33 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -96,4 +96,3 @@
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
-
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index ca33371..07a078f4 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -23,12 +23,12 @@
 #include <limits.h>
 #include <stdlib.h>
 
-using std::map;
-using std::unordered_map;
 using std::list;
 using std::make_shared;
+using std::map;
 using std::shared_ptr;
 using std::unique_ptr;
+using std::unordered_map;
 
 namespace android {
 namespace os {
@@ -36,51 +36,50 @@
 
 // ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
 ValueMetricProducer::ValueMetricProducer(const ValueMetric& metric, const int conditionIndex,
-                                         const sp<ConditionWizard>& wizard, const int pullTagId)
-    : MetricProducer((time(nullptr) / 600 * 600 * NANO_SECONDS_IN_A_SECOND), conditionIndex,
-                     wizard),
-      mMetric(metric),
-      mPullTagId(pullTagId) {
-  // TODO: valuemetric for pushed events may need unlimited bucket length
-  mBucketSizeNs = mMetric.bucket().bucket_size_millis() * 1000 * 1000;
+                                         const sp<ConditionWizard>& wizard, const int pullTagId,
+                                         const uint64_t startTimeNs)
+    : MetricProducer(startTimeNs, conditionIndex, wizard), mMetric(metric), mPullTagId(pullTagId) {
+    // TODO: valuemetric for pushed events may need unlimited bucket length
+    mBucketSizeNs = mMetric.bucket().bucket_size_millis() * 1000 * 1000;
 
-  mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
+    mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
 
-  if (metric.links().size() > 0) {
-    mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
-                           metric.links().end());
-    mConditionSliced = true;
-  }
+    if (metric.links().size() > 0) {
+        mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
+                               metric.links().end());
+        mConditionSliced = true;
+    }
 
-  if (!metric.has_condition() && mPullTagId != -1) {
-    mStatsPullerManager.RegisterReceiver(mPullTagId, this, metric.bucket().bucket_size_millis());
-  }
+    if (!metric.has_condition() && mPullTagId != -1) {
+        mStatsPullerManager.RegisterReceiver(mPullTagId, this,
+                                             metric.bucket().bucket_size_millis());
+    }
 
-  VLOG("value metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
-       (long long)mBucketSizeNs, (long long)mStartTimeNs);
+    VLOG("value metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
+         (long long)mBucketSizeNs, (long long)mStartTimeNs);
 }
 
 ValueMetricProducer::~ValueMetricProducer() {
-  VLOG("~ValueMetricProducer() called");
+    VLOG("~ValueMetricProducer() called");
 }
 
 void ValueMetricProducer::finish() {
-  // TODO: write the StatsLogReport to dropbox using
-  // DropboxWriter.
+    // TODO: write the StatsLogReport to dropbox using
+    // DropboxWriter.
 }
 
 static void addSlicedCounterToReport(StatsLogReport_ValueMetricDataWrapper& wrapper,
                                      const vector<KeyValuePair>& key,
                                      const vector<ValueBucketInfo>& buckets) {
-  ValueMetricData* data = wrapper.add_data();
-  for (const auto& kv : key) {
-    data->add_dimension()->CopyFrom(kv);
-  }
-  for (const auto& bucket : buckets) {
-    data->add_bucket_info()->CopyFrom(bucket);
-    VLOG("\t bucket [%lld - %lld] value: %lld", bucket.start_bucket_nanos(),
-         bucket.end_bucket_nanos(), bucket.value());
-  }
+    ValueMetricData* data = wrapper.add_data();
+    for (const auto& kv : key) {
+        data->add_dimension()->CopyFrom(kv);
+    }
+    for (const auto& bucket : buckets) {
+        data->add_bucket_info()->CopyFrom(bucket);
+        VLOG("\t bucket [%lld - %lld] value: %lld", bucket.start_bucket_nanos(),
+             bucket.end_bucket_nanos(), bucket.value());
+    }
 }
 
 void ValueMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
@@ -88,33 +87,28 @@
 }
 
 StatsLogReport ValueMetricProducer::onDumpReport() {
-  VLOG("metric %lld dump report now...", mMetric.metric_id());
+    VLOG("metric %lld dump report now...", mMetric.metric_id());
 
-  StatsLogReport report;
-  report.set_metric_id(mMetric.metric_id());
-  report.set_start_report_nanos(mStartTimeNs);
+    StatsLogReport report;
+    report.set_metric_id(mMetric.metric_id());
+    report.set_start_report_nanos(mStartTimeNs);
+    report.set_end_report_nanos(mCurrentBucketStartTimeNs);
 
-  // Dump current bucket if it's stale.
-  // If current bucket is still on-going, don't force dump current bucket.
-  // In finish(), We can force dump current bucket.
-  //    flush_if_needed(time(nullptr) * NANO_SECONDS_IN_A_SECOND);
-  report.set_end_report_nanos(mCurrentBucketStartTimeNs);
+    StatsLogReport_ValueMetricDataWrapper* wrapper = report.mutable_value_metrics();
 
-  StatsLogReport_ValueMetricDataWrapper* wrapper = report.mutable_value_metrics();
+    for (const auto& pair : mPastBuckets) {
+        const HashableDimensionKey& hashableKey = pair.first;
+        auto it = mDimensionKeyMap.find(hashableKey);
+        if (it == mDimensionKeyMap.end()) {
+            ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str());
+            continue;
+        }
 
-  for (const auto& pair : mPastBuckets) {
-    const HashableDimensionKey& hashableKey = pair.first;
-    auto it = mDimensionKeyMap.find(hashableKey);
-    if (it == mDimensionKeyMap.end()) {
-      ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str());
-      continue;
+        VLOG("  dimension key %s", hashableKey.c_str());
+        addSlicedCounterToReport(*wrapper, it->second, pair.second);
     }
-
-    VLOG("  dimension key %s", hashableKey.c_str());
-    addSlicedCounterToReport(*wrapper, it->second, pair.second);
-  }
-  return report;
-  // TODO: Clear mPastBuckets, mDimensionKeyMap once the report is dumped.
+    return report;
+    // TODO: Clear mPastBuckets, mDimensionKeyMap once the report is dumped.
 }
 
 void ValueMetricProducer::onConditionChanged(const bool condition, const uint64_t eventTime) {
@@ -158,50 +152,50 @@
 }
 
 void ValueMetricProducer::onMatchedLogEventInternal(
-    const size_t matcherIndex, const HashableDimensionKey& eventKey,
-    const map<string, HashableDimensionKey>& conditionKey, bool condition,
-    const LogEvent& event, bool scheduledPull) {
-  uint64_t eventTimeNs = event.GetTimestampNs();
-  if (eventTimeNs < mCurrentBucketStartTimeNs) {
-      VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
-           (long long)mCurrentBucketStartTimeNs);
-      return;
-  }
-
-  Interval& interval = mCurrentSlicedBucket[eventKey];
-
-  long value = get_value(event);
-
-  if (scheduledPull) {
-    if (interval.raw.size() > 0) {
-      interval.raw.back().second = value;
-    } else {
-      interval.raw.push_back(std::make_pair(value, value));
+        const size_t matcherIndex, const HashableDimensionKey& eventKey,
+        const map<string, HashableDimensionKey>& conditionKey, bool condition,
+        const LogEvent& event, bool scheduledPull) {
+    uint64_t eventTimeNs = event.GetTimestampNs();
+    if (eventTimeNs < mCurrentBucketStartTimeNs) {
+        VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
+             (long long)mCurrentBucketStartTimeNs);
+        return;
     }
-    mNextSlicedBucket[eventKey].raw[0].first = value;
-  } else {
-    if (mCondition == ConditionState::kTrue) {
-      interval.raw.push_back(std::make_pair(value, 0));
+
+    Interval& interval = mCurrentSlicedBucket[eventKey];
+
+    long value = get_value(event);
+
+    if (scheduledPull) {
+        if (interval.raw.size() > 0) {
+            interval.raw.back().second = value;
+        } else {
+            interval.raw.push_back(std::make_pair(value, value));
+        }
+        mNextSlicedBucket[eventKey].raw[0].first = value;
     } else {
-      if (interval.raw.size() != 0) {
-        interval.raw.back().second = value;
-      }
+        if (mCondition == ConditionState::kTrue) {
+            interval.raw.push_back(std::make_pair(value, 0));
+        } else {
+            if (interval.raw.size() != 0) {
+                interval.raw.back().second = value;
+            }
+        }
     }
-  }
-  if (mPullTagId == -1) {
-      flush_if_needed(eventTimeNs);
-  }
+    if (mPullTagId == -1) {
+        flush_if_needed(eventTimeNs);
+    }
 }
 
 long ValueMetricProducer::get_value(const LogEvent& event) {
-  status_t err = NO_ERROR;
-  long val = event.GetLong(mMetric.value_field(), &err);
-  if (err == NO_ERROR) {
-    return val;
-  } else {
-    VLOG("Can't find value in message.");
-    return 0;
-  }
+    status_t err = NO_ERROR;
+    long val = event.GetLong(mMetric.value_field(), &err);
+    if (err == NO_ERROR) {
+        return val;
+    } else {
+        VLOG("Can't find value in message.");
+        return 0;
+    }
 }
 
 void ValueMetricProducer::flush_if_needed(const uint64_t eventTimeNs) {
@@ -218,22 +212,22 @@
     info.set_end_bucket_nanos(mCurrentBucketStartTimeNs + mBucketSizeNs);
 
     for (const auto& slice : mCurrentSlicedBucket) {
-      long value = 0;
-      for (const auto& pair : slice.second.raw) {
-        value += pair.second - pair.first;
-      }
-      info.set_value(value);
-      VLOG(" %s, %ld", slice.first.c_str(), value);
-      // it will auto create new vector of ValuebucketInfo if the key is not found.
-      auto& bucketList = mPastBuckets[slice.first];
-      bucketList.push_back(info);
+        long value = 0;
+        for (const auto& pair : slice.second.raw) {
+            value += pair.second - pair.first;
+        }
+        info.set_value(value);
+        VLOG(" %s, %ld", slice.first.c_str(), value);
+        // it will auto create new vector of ValuebucketInfo if the key is not found.
+        auto& bucketList = mPastBuckets[slice.first];
+        bucketList.push_back(info);
     }
 
     // Reset counters
     mCurrentSlicedBucket.swap(mNextSlicedBucket);
     mNextSlicedBucket.clear();
     int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
-    if (numBucketsForward >1) {
+    if (numBucketsForward > 1) {
         VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
     }
     mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
@@ -243,4 +237,4 @@
 
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 8653981..548cd44 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -33,7 +33,8 @@
 class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
 public:
     ValueMetricProducer(const ValueMetric& valueMetric, const int conditionIndex,
-                        const sp<ConditionWizard>& wizard, const int pullTagId);
+                        const sp<ConditionWizard>& wizard, const int pullTagId,
+                        const uint64_t startTimeNs);
 
     virtual ~ValueMetricProducer();
 
@@ -47,7 +48,9 @@
 
     void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) override;
     // TODO: Implement this later.
-    size_t byteSize() override{return 0;};
+    size_t byteSize() override {
+        return 0;
+    };
 
     // TODO: Implement this later.
     virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index a0fdcdc..ca9cdfb 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -192,10 +192,11 @@
                  unordered_map<int, std::vector<int>>& conditionToMetricMap,
                  unordered_map<int, std::vector<int>>& trackerToMetricMap) {
     sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
-    const int allMetricsCount =
-            config.count_metric_size() + config.duration_metric_size() + config.event_metric_size() + config.value_metric_size();
+    const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
+                                config.event_metric_size() + config.value_metric_size();
     allMetricProducers.reserve(allMetricsCount);
     StatsPullerManager& statsPullerManager = StatsPullerManager::GetInstance();
+    uint64_t startTimeNs = time(nullptr) * NS_PER_SEC;
 
     // Build MetricProducers for each metric defined in config.
     // build CountMetricProducer
@@ -221,7 +222,8 @@
                                        conditionToMetricMap);
         }
 
-        sp<MetricProducer> countProducer = new CountMetricProducer(metric, conditionIndex, wizard);
+        sp<MetricProducer> countProducer =
+                new CountMetricProducer(metric, conditionIndex, wizard, startTimeNs);
         allMetricProducers.push_back(countProducer);
     }
 
@@ -282,7 +284,7 @@
 
         sp<MetricProducer> durationMetric = new DurationMetricProducer(
                 metric, conditionIndex, trackerIndices[0], trackerIndices[1], trackerIndices[2],
-                wizard, internalDimension);
+                wizard, internalDimension, startTimeNs);
 
         allMetricProducers.push_back(durationMetric);
     }
@@ -308,7 +310,9 @@
                                        conditionToMetricMap);
         }
 
-        sp<MetricProducer> eventMetric = new EventMetricProducer(metric, conditionIndex, wizard);
+        sp<MetricProducer> eventMetric =
+                new EventMetricProducer(metric, conditionIndex, wizard, startTimeNs);
+
         allMetricProducers.push_back(eventMetric);
     }
 
@@ -348,7 +352,7 @@
         }
 
         sp<MetricProducer> valueProducer =
-                new ValueMetricProducer(metric, conditionIndex, wizard, pullTagId);
+                new ValueMetricProducer(metric, conditionIndex, wizard, pullTagId, startTimeNs);
         allMetricProducers.push_back(valueProducer);
     }
 
diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h
index d3d7e37..a9507bf 100644
--- a/cmds/statsd/src/stats_util.h
+++ b/cmds/statsd/src/stats_util.h
@@ -28,7 +28,6 @@
 
 #define DEFAULT_DIMENSION_KEY ""
 #define MATCHER_NOT_FOUND -2
-#define NANO_SECONDS_IN_A_SECOND (1000 * 1000 * 1000)
 
 typedef std::string HashableDimensionKey;
 
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index fdfe8ef..d0898b0 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -42,7 +42,7 @@
     auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
     simpleMatcher->set_tag(TAG_ID);
 
-    LogEvent event(TAG_ID);
+    LogEvent event(TAG_ID, 0);
 
     // Convert to a LogEvent
     event.init();
@@ -62,7 +62,7 @@
     keyValue2->mutable_key_matcher()->set_key(FIELD_ID_2);
 
     // Set up the event
-    LogEvent event(TAG_ID);
+    LogEvent event(TAG_ID, 0);
     auto list = event.GetAndroidLogEventList();
     *list << true;
     *list << false;
@@ -98,7 +98,7 @@
     keyValue->set_eq_string("some value");
 
     // Set up the event
-    LogEvent event(TAG_ID);
+    LogEvent event(TAG_ID, 0);
     auto list = event.GetAndroidLogEventList();
     *list << "some value";
 
@@ -119,7 +119,7 @@
     keyValue->mutable_key_matcher()->set_key(FIELD_ID_1);
 
     // Set up the event
-    LogEvent event(TAG_ID);
+    LogEvent event(TAG_ID, 0);
     auto list = event.GetAndroidLogEventList();
     *list << 11;
 
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
new file mode 100644
index 0000000..5a4ee73
--- /dev/null
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -0,0 +1,183 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "metrics_test_helper.h"
+#include "src/metrics/CountMetricProducer.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <vector>
+
+using namespace testing;
+using android::sp;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+TEST(CountMetricProducerTest, TestNonDimensionalEvents) {
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+    int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+    int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
+    int tagId = 1;
+
+    CountMetric metric;
+    metric.set_metric_id(1);
+    metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+
+    LogEvent event1(tagId, bucketStartTimeNs + 1);
+    LogEvent event2(tagId, bucketStartTimeNs + 2);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    CountMetricProducer countProducer(metric, -1 /*-1 meaning no condition*/, wizard,
+                                      bucketStartTimeNs);
+
+    // 2 events in bucket 1.
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1, false);
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2, false);
+    countProducer.flushCounterIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+    EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
+    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+                countProducer.mPastBuckets.end());
+    const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+    EXPECT_EQ(1UL, buckets.size());
+    const auto& bucketInfo = buckets[0];
+    EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
+    EXPECT_EQ(2LL, bucketInfo.mCount);
+
+    // 1 matched event happens in bucket 2.
+    LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 2);
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3, false);
+    countProducer.flushCounterIfNeeded(bucketStartTimeNs + 2 * bucketSizeNs + 1);
+    EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
+    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+                countProducer.mPastBuckets.end());
+    EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY].size());
+    const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY][1];
+    EXPECT_EQ(bucket2StartTimeNs, bucketInfo2.mBucketStartNs);
+    EXPECT_EQ(bucket2StartTimeNs + bucketSizeNs, bucketInfo2.mBucketEndNs);
+    EXPECT_EQ(1LL, bucketInfo2.mCount);
+
+    // nothing happens in bucket 3. we should not record anything for bucket 3.
+    countProducer.flushCounterIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 1);
+    EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
+    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+                countProducer.mPastBuckets.end());
+    const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+    EXPECT_EQ(2UL, buckets3.size());
+}
+
+TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) {
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+    CountMetric metric;
+    metric.set_metric_id(1);
+    metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+    metric.set_condition("SCREEN_ON");
+
+    LogEvent event1(1, bucketStartTimeNs + 1);
+    LogEvent event2(1, bucketStartTimeNs + 10);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    CountMetricProducer countProducer(metric, 1, wizard, bucketStartTimeNs);
+
+    countProducer.onConditionChanged(true, bucketStartTimeNs);
+    countProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/);
+    EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
+
+    countProducer.onConditionChanged(false /*new condition*/, bucketStartTimeNs + 2);
+    countProducer.onMatchedLogEvent(1 /*matcher index*/, event2, false /*pulled*/);
+    EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
+
+    countProducer.flushCounterIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+
+    EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
+    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+                countProducer.mPastBuckets.end());
+    const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+    EXPECT_EQ(1UL, buckets.size());
+    const auto& bucketInfo = buckets[0];
+    EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
+    EXPECT_EQ(1LL, bucketInfo.mCount);
+}
+
+TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+    CountMetric metric;
+    metric.set_metric_id(1);
+    metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+    metric.set_condition("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON");
+    EventConditionLink* link = metric.add_links();
+    link->set_condition("APP_IN_BACKGROUND_PER_UID");
+    link->add_key_in_main()->set_key(1);
+    link->add_key_in_condition()->set_key(2);
+
+    LogEvent event1(1, bucketStartTimeNs + 1);
+    auto list = event1.GetAndroidLogEventList();
+    *list << "111";  // uid
+    event1.init();
+    ConditionKey key1;
+    key1["APP_IN_BACKGROUND_PER_UID"] = "2:111|";
+
+    LogEvent event2(1, bucketStartTimeNs + 10);
+    auto list2 = event2.GetAndroidLogEventList();
+    *list2 << "222";  // uid
+    event2.init();
+    ConditionKey key2;
+    key2["APP_IN_BACKGROUND_PER_UID"] = "2:222|";
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse));
+
+    EXPECT_CALL(*wizard, query(_, key2)).WillOnce(Return(ConditionState::kTrue));
+
+    CountMetricProducer countProducer(metric, 1 /*condition tracker index*/, wizard,
+                                      bucketStartTimeNs);
+
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1, false);
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2, false);
+
+    countProducer.flushCounterIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+
+    EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
+    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+                countProducer.mPastBuckets.end());
+    const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+    EXPECT_EQ(1UL, buckets.size());
+    const auto& bucketInfo = buckets[0];
+    EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
+    EXPECT_EQ(1LL, bucketInfo.mCount);
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
new file mode 100644
index 0000000..76dbc73
--- /dev/null
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -0,0 +1,130 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "metrics_test_helper.h"
+#include "src/metrics/EventMetricProducer.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <vector>
+
+using namespace testing;
+using android::sp;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+TEST(EventMetricProducerTest, TestNoCondition) {
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+    uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+    EventMetric metric;
+    metric.set_metric_id(1);
+
+    LogEvent event1(1 /*tag id*/, bucketStartTimeNs + 1);
+    LogEvent event2(1 /*tag id*/, bucketStartTimeNs + 2);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    EventMetricProducer eventProducer(metric, -1 /*-1 meaning no condition*/, wizard,
+                                      bucketStartTimeNs);
+
+    eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/);
+    eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2, false /*pulled*/);
+
+    // TODO: get the report and check the content after the ProtoOutputStream change is done.
+    // eventProducer.onDumpReport();
+}
+
+TEST(EventMetricProducerTest, TestEventsWithNonSlicedCondition) {
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+    uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+    EventMetric metric;
+    metric.set_metric_id(1);
+    metric.set_condition("SCREEN_ON");
+
+    LogEvent event1(1, bucketStartTimeNs + 1);
+    LogEvent event2(1, bucketStartTimeNs + 10);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    EventMetricProducer eventProducer(metric, 1, wizard, bucketStartTimeNs);
+
+    eventProducer.onConditionChanged(true /*condition*/, bucketStartTimeNs);
+    eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/);
+
+    eventProducer.onConditionChanged(false /*condition*/, bucketStartTimeNs + 2);
+
+    eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2, false /*pulled*/);
+
+    // TODO: get the report and check the content after the ProtoOutputStream change is done.
+    // eventProducer.onDumpReport();
+}
+
+TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) {
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+    EventMetric metric;
+    metric.set_metric_id(1);
+    metric.set_condition("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON");
+    EventConditionLink* link = metric.add_links();
+    link->set_condition("APP_IN_BACKGROUND_PER_UID");
+    link->add_key_in_main()->set_key(1);
+    link->add_key_in_condition()->set_key(2);
+
+    LogEvent event1(1, bucketStartTimeNs + 1);
+    auto list = event1.GetAndroidLogEventList();
+    *list << "111";  // uid
+    event1.init();
+    ConditionKey key1;
+    key1["APP_IN_BACKGROUND_PER_UID"] = "2:111|";
+
+    LogEvent event2(1, bucketStartTimeNs + 10);
+    auto list2 = event2.GetAndroidLogEventList();
+    *list2 << "222";  // uid
+    event2.init();
+    ConditionKey key2;
+    key2["APP_IN_BACKGROUND_PER_UID"] = "2:222|";
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse));
+
+    EXPECT_CALL(*wizard, query(_, key2)).WillOnce(Return(ConditionState::kTrue));
+
+    EventMetricProducer eventProducer(metric, 1, wizard, bucketStartTimeNs);
+
+    eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/);
+    eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2, false /*pulled*/);
+
+    // TODO: get the report and check the content after the ProtoOutputStream change is done.
+    // eventProducer.onDumpReport();
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
similarity index 92%
rename from cmds/statsd/tests/MaxDurationTracker_test.cpp
rename to cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index ae8bf42..f2abe7b 100644
--- a/cmds/statsd/tests/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -12,12 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
+#include "metrics_test_helper.h"
 #include "src/condition/ConditionWizard.h"
 #include "src/metrics/duration_helper/MaxDurationTracker.h"
 
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
 #include <stdio.h>
 #include <set>
 #include <unordered_map>
@@ -32,13 +32,9 @@
 
 #ifdef __ANDROID__
 
-class MockConditionWizard : public ConditionWizard {
-public:
-    MOCK_METHOD2(
-            query,
-            ConditionState(const int conditionIndex,
-                           const std::map<std::string, HashableDimensionKey>& conditionParameters));
-};
+namespace android {
+namespace os {
+namespace statsd {
 
 TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
@@ -110,6 +106,9 @@
     EXPECT_EQ(5, buckets[0].duration_nanos());
 }
 
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
diff --git a/cmds/statsd/tests/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
similarity index 90%
rename from cmds/statsd/tests/OringDurationTracker_test.cpp
rename to cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index 0b79819..338d55d 100644
--- a/cmds/statsd/tests/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -12,18 +12,17 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
+#include "metrics_test_helper.h"
 #include "src/condition/ConditionWizard.h"
 #include "src/metrics/duration_helper/OringDurationTracker.h"
 
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
 #include <stdio.h>
 #include <set>
 #include <unordered_map>
 #include <vector>
 
-using namespace android::os::statsd;
 using namespace testing;
 using android::sp;
 using std::set;
@@ -31,14 +30,9 @@
 using std::vector;
 
 #ifdef __ANDROID__
-
-class MockConditionWizard : public ConditionWizard {
-public:
-    MOCK_METHOD2(
-            query,
-            ConditionState(const int conditionIndex,
-                           const std::map<std::string, HashableDimensionKey>& conditionParameters));
-};
+namespace android {
+namespace os {
+namespace statsd {
 
 TEST(OringDurationTrackerTest, TestDurationOverlap) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
@@ -93,7 +87,9 @@
     EXPECT_EQ(1u, buckets.size());
     EXPECT_EQ(5, buckets[0].duration_nanos());
 }
-
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
new file mode 100644
index 0000000..5fd7d62
--- /dev/null
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -0,0 +1,35 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#pragma once
+
+#include "src/condition/ConditionWizard.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class MockConditionWizard : public ConditionWizard {
+public:
+    MOCK_METHOD2(
+            query,
+            ConditionState(const int conditionIndex,
+                           const std::map<std::string, HashableDimensionKey>& conditionParameters));
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android