Add a DropboxWriter in statsd.

+ The DropboxWriter keeps data in cache, and flush to files once the
  size exceeds the maximum value.

+ Different components should create their owner DropboxWriter with
  different tags, e.g., anomly detection, experiment metrics, etc.

+ Copied stats_log related protos from g3

Test: run statsd, and adb shell dumpsys dropbox
      Will add unit tests.

Change-Id: If06e9a9953be32082252b340a97124d732656b40
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index db8c89d..0e6d292 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -14,6 +14,23 @@
 
 LOCAL_PATH:= $(call my-dir)
 
+# ================
+# proto static lib
+# ================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := statsd_proto
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-proto-files-under, src)
+
+LOCAL_PROTOC_FLAGS :=
+LOCAL_PROTOC_OPTIMIZE_TYPE := lite
+
+include $(BUILD_STATIC_LIBRARY)
+
+STATSD_PROTO_INCLUDES := $(local-generated-sources-dir)/src/$(LOCAL_PATH)
+
 # =========
 # statsd
 # =========
@@ -27,7 +44,12 @@
     src/StatsService.cpp \
     src/LogEntryPrinter.cpp \
     src/LogReader.cpp \
-    src/main.cpp
+    src/main.cpp \
+    src/DropboxWriter.cpp \
+    src/StatsLogProcessor.cpp \
+    src/stats_log.proto \
+    src/statsd_config.proto \
+    src/stats_constants.proto \
 
 LOCAL_CFLAGS += \
     -Wall \
@@ -47,7 +69,10 @@
 endif
 
 LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/../../core/java
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/src \
+	STATSD_PROTO_INCLUDES
+
+LOCAL_STATIC_LIBRARIES := statsd_proto
 
 LOCAL_SHARED_LIBRARIES := \
         libbase \
@@ -56,7 +81,8 @@
         libincident \
         liblog \
         libselinux \
-        libutils
+        libutils \
+        libservices \
 
 LOCAL_MODULE_CLASS := EXECUTABLES
 
@@ -82,7 +108,8 @@
     -Wno-unused-function \
     -Wno-unused-parameter
 
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/src \
+	STATSD_PROTO_INCLUDES
 
 LOCAL_SRC_FILES := \
     ../../core/java/android/os/IStatsManager.aidl \
@@ -93,6 +120,7 @@
 
 LOCAL_STATIC_LIBRARIES := \
     libgmock \
+    statsd_proto
 
 LOCAL_SHARED_LIBRARIES := \
     libbase \
@@ -103,4 +131,3 @@
     libutils
 
 include $(BUILD_NATIVE_TEST)
-
diff --git a/cmds/statsd/src/DropboxWriter.cpp b/cmds/statsd/src/DropboxWriter.cpp
new file mode 100644
index 0000000..a251056
--- /dev/null
+++ b/cmds/statsd/src/DropboxWriter.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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 <android/os/DropBoxManager.h>
+#include <cutils/log.h>
+
+#include "DropboxWriter.h"
+
+using android::os::DropBoxManager;
+using android::binder::Status;
+using android::sp;
+using android::String16;
+using std::vector;
+
+DropboxWriter::DropboxWriter(const string& tag)
+    : mTag(tag), mLogList(), mBufferSize(0) {
+}
+
+void DropboxWriter::addEntry(const StatsLogEntry& entry) {
+    flushIfNecessary(entry);
+    StatsLogEntry* newEntry = mLogList.add_stats_log_entry();
+    newEntry->CopyFrom(entry);
+    mBufferSize += entry.ByteSize();
+}
+
+void DropboxWriter::flushIfNecessary(const StatsLogEntry& entry) {
+    // The serialized size of the StatsLogList is approximately the sum of the serialized size of
+    // every StatsLogEntry inside it.
+    if (entry.ByteSize() + mBufferSize > kMaxSerializedBytes) {
+        flush();
+    }
+}
+
+void DropboxWriter::flush() {
+    // now we get an exact byte size of the output
+    const int numBytes = mLogList.ByteSize();
+    vector<uint8_t> buffer(numBytes);
+    sp<DropBoxManager> dropbox = new DropBoxManager();
+    mLogList.SerializeToArray(&buffer[0], numBytes);
+    Status status = dropbox->addData(String16(mTag.c_str()), &buffer[0],
+            numBytes, 0 /* no flag */);
+    if (!status.isOk()) {
+        ALOGE("failed to write to dropbox");
+        //TODO: What to do if flush fails??
+    }
+    mLogList.Clear();
+    mBufferSize = 0;
+}
diff --git a/cmds/statsd/src/DropboxWriter.h b/cmds/statsd/src/DropboxWriter.h
new file mode 100644
index 0000000..176ac8b
--- /dev/null
+++ b/cmds/statsd/src/DropboxWriter.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef DROPBOX_WRITER_H
+#define DROPBOX_WRITER_H
+
+#include <frameworks/base/cmds/statsd/src/stats_log.pb.h>
+
+using std::string;
+using android::os::statsd::StatsLogEntry;
+using android::os::statsd::StatsLogList;
+
+class DropboxWriter {
+public:
+    /* tag will be part of the file name, and used as the key to build the file index inside
+       DropBoxManagerService.
+     */
+    DropboxWriter(const string& tag);
+
+    void addEntry(const StatsLogEntry& entry);
+
+    /* Request a flush to dropbox. */
+    void flush();
+
+private:
+    /* Max *serialized* size of the logs kept in memory before flushing to dropbox.
+       Proto lite does not implement the SpaceUsed() function which gives the in memory byte size.
+       So we cap memory usage by limiting the serialized size. Note that protobuf's in memory size
+       is higher than its serialized size. DropboxManager will compress the file when the data is
+       larger than 4KB. So the final file size is less than this number.
+     */
+    static const size_t kMaxSerializedBytes = 16 * 1024;
+
+    const string mTag;
+
+    /* StatsLogList is a wrapper for storing a list of StatsLogEntry */
+    StatsLogList mLogList;
+
+    /* Current *serialized* size of the logs kept in memory.
+       To save computation, we will not calculate the size of the StatsLogList every time when a new
+       entry is added, which would recursively call ByteSize() on every log entry. Instead, we keep
+       the sum of all individual stats log entry sizes. The size of a proto is approximately the sum
+       of the size of all member protos.
+     */
+    size_t mBufferSize = 0;
+
+    /* Check if the buffer size exceeds the max buffer size when the new entry is added, and flush
+       the logs to dropbox if true. */
+    void flushIfNecessary(const StatsLogEntry& entry);
+
+};
+
+#endif //DROPBOX_WRITER_H
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
new file mode 100644
index 0000000..f49dfde
--- /dev/null
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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 <StatsLogProcessor.h>
+
+#include <log/event_tag_map.h>
+#include <log/logprint.h>
+#include <utils/Errors.h>
+
+#include <frameworks/base/cmds/statsd/src/stats_log.pb.h>
+
+using namespace android;
+using android::os::statsd::StatsLogEntry;
+
+StatsLogProcessor::StatsLogProcessor() : m_dropbox_writer("all-logs")
+{
+    // Initialize the EventTagMap, which is how we know the names of the numeric event tags.
+    // If this fails, we can't print well, but something will print.
+    m_tags = android_openEventTagMap(NULL);
+
+    // Printing format
+    m_format = android_log_format_new();
+    android_log_setPrintFormat(m_format, FORMAT_THREADTIME);
+}
+
+StatsLogProcessor::~StatsLogProcessor()
+{
+    if (m_tags != NULL) {
+        android_closeEventTagMap(m_tags);
+    }
+    android_log_format_free(m_format);
+}
+
+void
+StatsLogProcessor::OnLogEvent(const log_msg& msg)
+{
+    status_t err;
+    AndroidLogEntry entry;
+    char buf[1024];
+
+    err = android_log_processBinaryLogBuffer(&(const_cast<log_msg*>(&msg)->entry_v1),
+                &entry, m_tags, buf, sizeof(buf));
+
+    // dump all statsd logs to dropbox for now.
+    // TODO: Add filtering, aggregation, etc.
+    if (err == NO_ERROR) {
+        StatsLogEntry logEntry;
+        logEntry.set_uid(entry.uid);
+        logEntry.set_pid(entry.pid);
+        logEntry.set_start_report_millis(entry.tv_sec / 1000 + entry.tv_nsec / 1000 / 1000);
+        logEntry.add_pairs()->set_value_str(entry.message, entry.messageLen);
+        m_dropbox_writer.addEntry(logEntry);
+    }
+}
+
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
new file mode 100644
index 0000000..23066ab9
--- /dev/null
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+#ifndef STATS_LOG_PROCESSOR_H
+#define STATS_LOG_PROCESSOR_H
+
+#include "LogReader.h"
+#include "DropboxWriter.h"
+
+#include <log/logprint.h>
+#include <stdio.h>
+
+class StatsLogProcessor : public LogListener
+{
+public:
+    StatsLogProcessor();
+    virtual ~StatsLogProcessor();
+
+    virtual void OnLogEvent(const log_msg& msg);
+
+private:
+    /**
+     * Numeric to string tag name mapping.
+     */
+    EventTagMap* m_tags;
+
+    /**
+     * Pretty printing format.
+     */
+    AndroidLogFormat* m_format;
+
+    DropboxWriter m_dropbox_writer;
+};
+#endif //STATS_LOG_PROCESSOR_H
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index 93405cb..2c721c7 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -19,6 +19,7 @@
 #include "LogEntryPrinter.h"
 #include "LogReader.h"
 #include "StatsService.h"
+#include "StatsLogProcessor.h"
 
 #include <binder/IInterface.h>
 #include <binder/IPCThreadState.h>
@@ -55,9 +56,8 @@
     sp<LogReader> reader = new LogReader();
 
     // Put the printer one first, so it will print before the real ones.
-    if (true) {
-        reader->AddListener(new LogEntryPrinter(STDOUT_FILENO));
-    }
+    reader->AddListener(new LogEntryPrinter(STDOUT_FILENO));
+    reader->AddListener(new StatsLogProcessor());
 
     // TODO: Construct and add real LogListners here.
 
diff --git a/cmds/statsd/src/stats_constants.proto b/cmds/statsd/src/stats_constants.proto
new file mode 100644
index 0000000..1787ae3
--- /dev/null
+++ b/cmds/statsd/src/stats_constants.proto
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+syntax = "proto2";
+
+package android.os.statsd;
+
+option optimize_for = LITE_RUNTIME;
+
+option java_package = "com.android.internal.logging";
+option java_outer_classname = "StatsConstantsProto";
+
+message StatsConstants {
+  // Event type.
+  enum Type {
+    WAKELOCK = 1;
+    SCREEN= 2;
+  }
+}
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
new file mode 100644
index 0000000..ec92023
--- /dev/null
+++ b/cmds/statsd/src/stats_log.proto
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+syntax = "proto2";
+
+package android.os.statsd;
+
+option optimize_for = LITE_RUNTIME;
+
+option java_package = "com.android.os";
+option java_outer_classname = "StatsLog";
+
+import "frameworks/base/cmds/statsd/src/statsd_config.proto";
+import "frameworks/base/cmds/statsd/src/stats_constants.proto";
+
+// StatsLogEntry is a generic proto holding a single metrics data.
+message StatsLogEntry {
+  // Type of stats.
+  optional android.os.statsd.StatsConstants.Type type = 1;
+
+  // Aggregation type of the data.
+  optional android.os.statsd.TrackedAggregateType aggregate_type = 2;
+
+  // Start timestamp of the interval. Timestamp for event-type data will have
+  // equal value for start_report_millis and end_report_millis.
+  optional int64 start_report_millis = 3;
+
+  // End timestamp of the interval.
+  optional int64 end_report_millis = 4;
+
+  // Package information for application-level data.
+  optional string package_name = 5;
+  optional int32 package_version = 6;
+  optional string package_version_string = 7;
+
+  // UID associated with the data.
+  optional int32 uid = 8;
+
+  // PID associated with the data.
+  optional int32 pid = 9;
+
+  // Payload contains key value pairs of the data from statsd.
+  message KeyValuePair {
+    // Integer representation of data type.
+    optional int32 key = 1;
+
+    oneof value {
+      string value_str = 2;
+      int64 value_int = 3;
+      bool value_bool = 4;
+    }
+  }
+  repeated KeyValuePair pairs = 10;
+
+  // Next tag: 11
+}
+
+// Data captured for a given metric during a given period of time.
+message StatsLogList {
+  // Unique ID for this metric.
+  optional int32 metric_id = 1;
+
+  // List of stats log entry.
+  repeated StatsLogEntry stats_log_entry = 2;
+}
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
new file mode 100644
index 0000000..2d034e5
--- /dev/null
+++ b/cmds/statsd/src/statsd_config.proto
@@ -0,0 +1,449 @@
+/*
+ * 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.
+ */
+
+
+// Version 1.
+// Important: Update the version line above before copy-pasting this file
+// from/to Google3 and Android repository.
+// This proto needs to be manually synced between Google3 and Android versions.
+
+/*
+ * Note about semantics of the buckets:
+ * In this current proto scheme, the buckets are updated only when an event
+ * occurs. In the case of durations, this means that we update at the end of a
+ * duration.
+ *
+ * For example, suppose we have buckets at every 10 min:
+ * 0, 10, 20, 30, 40, etc.
+ * And then suppose a wakelock is first held starting at min 5 and lasts for 21
+ * mins. Then the buckets for 0-10 and 10-20 don't contain anything and inside
+ * the bucket for 20-30, we add the value of 21 minutes.
+ *
+ * Also note that buckets are only aligned to wall-clock (no custom time-bases).
+ */
+
+syntax = "proto2";
+package android.os.statsd;
+
+option optimize_for = LITE_RUNTIME;
+
+option java_package = "com.android.internal.os";
+option java_outer_classname = "StatsdConfigProto";
+
+// KeyMatcher specifies how to match the key.
+message KeyMatcher {
+  oneof contents {
+    int32 key = 1; // ID of the key to match.
+
+    // Special matcher for package name. This will match either the package name
+    // or the UID (statsD will map the UID of the source event to a package
+    // name). Specify the package name to match in eq_string.
+    bool use_package = 2;
+  }
+}
+
+// FieldMatcher allows us to match specific fields/keys in an event.
+message FieldMatcher {
+  optional KeyMatcher key_matcher = 1;
+
+  oneof value_matcher {
+    // Equality matchers
+    bool eq_bool = 2;
+    string eq_string = 3;
+    int32 eq_int32 = 4;
+    int64 eq_int64 = 5;
+
+    // Numeric comparisons;
+    int32 lt_int32 = 6;
+    int32 gt_int32 = 7;
+    int64 lt_int64 = 8;
+    int64 gt_int64 = 9;
+    float lt_float = 10;
+    float gt_float = 11;
+  }
+}
+
+enum OperationType {
+  AND = 1;
+  OR = 2;
+  NOT = 3; // Must have only a single operand when using NOT operator.
+  NAND = 4;  // NAND and NOR as conveniences to avoid NOT+(AND/OR)-layers.
+  NOR = 5;
+}
+
+enum TrackedAggregateType {
+  // IS_RUNNING; // whether it is currently running
+  VALUE_COUNT = 1;  // count number of events
+  VALUE_SUM = 2;
+  VALUE_MAX = 3;
+  VALUE_MIN = 4;
+  DURATION_SUM = 5;      // cumulative total time
+  DURATION_MAX = 6;      // longest continuously-on time
+  DURATION_MIN = 7;      // shortest continuously-on time
+  //DURATION_CURRENT = 6;  // current continuously-on time (not bucketed)
+}
+
+// Assume the events come in with a tag and an array of (key, value) tuples
+// where the key must be an int32 and value can be any type.
+message LineMatcher {
+  // For now, we assume that we don't flatten the tags (ie, one tag corresponds
+  // to screen-on and screen-off events and key 1 represents ON or OFF).
+  repeated int32 tag = 1; // Must match at least one of the tags.
+
+  message Nested {
+    optional OperationType operation = 1;
+    repeated LineMatcher matcher = 2;
+  }
+  oneof contents {
+    FieldMatcher requirement = 2;
+    Nested nested = 3;
+  }
+}
+
+// Defines when an AggregateCounter or EventMatcher applies.
+message Condition {
+  message Nested {
+    optional OperationType operation = 1;
+    repeated Condition nested_conditions = 2;  // operands that are themselves
+                                               // conditions (recursively)
+  }
+
+  // Leaf node of condition.
+  message RangeMatcher {
+    optional LineMatcher start = 1;
+    optional LineMatcher stop = 2;
+    optional bool count_nesting = 3
+        [default = true];  // true if "start start stop" is still
+                            // considered running
+
+    // Configure which fields define the slices. These fields must be present in
+    // both the start and stop lines. Note that this can be a subset of all the
+    // slices defined in the AggregateCounter.
+    // For example, if the counter slices on both app name and wake lock name,
+    // we can define that this range only slices on app name.
+    repeated KeyMatcher slice = 4;
+  }
+
+  oneof contents {
+    RangeMatcher range = 1; // Represents a leaf node.
+    Nested nested = 2; // Represents a non-leaf node.
+  }
+}
+
+// Emits matching events to statsd event buffer.
+message EventMatcher {
+  // Tracks what configuration led to uploading of this event.
+  optional int32 metric_id = 1;
+
+  // LineMatcher for the event to emit.
+  optional LineMatcher what = 2;
+
+  optional Condition condition = 3;
+
+  // TODO: Have a clear use-case for this in P or-else drop this for P.
+  message Filter {
+  }
+  optional Filter filter = 4;
+}
+
+// Hard-code the possible metrics that we can pull.
+// For example, NETSTATS_BY_UID would provide network usage per uid.
+// We should treat the results like a batch of individual log events, and we
+// should process them one-by-one to re-use our LineMatcher logic.
+enum PulledMetricSource {
+  NETSTATS = 1;
+}
+
+message AggregateCounter {  // previously called Timer
+  // Specifies which fields in the message act as dimensions.
+  // For both pushed and pulled metrics, we assume every record has all the
+  // dimensions set.
+  message Slicer {
+    repeated KeyMatcher keys = 1;
+  }
+  optional Slicer slicer = 1;
+
+  message ValueSource {
+    message PushedMetric {
+      // LineMatcher for the event to apply.
+      // Slicing (which keys act as dimensions) should not be specified here.
+      optional LineMatcher what = 1;
+
+      // Only needed if one key should be treated as the value.
+      optional int32 value_key = 2;
+    }
+
+    // The values for pulled metrics are computed and aggregated at the end of
+    // the condition.
+    message PulledMetric {
+      optional bool compute_diff =
+          1;  // If we want the diff (if this
+              // metric is pulled when condition opens/closes).
+      optional PulledMetricSource metric = 2;
+
+      // We treat the pulled metrics as a batch of log-records that look like
+      // they came from LogD.
+      optional LineMatcher what = 3;
+      optional int32 value_field = 4;
+    }
+
+    oneof value {
+      PushedMetric pushed_metric = 1;
+
+      // Pulled metrics are computed when the duration closes (and are also
+      // fetched at the open if we need to compute a diff).
+      // Pulled metrics require a condition being defined.
+      // These metrics are not pulled at the end of every bucket.
+      PulledMetric pulled_metric = 2;
+
+      // Polled Metrics are pulled at the end of every bucket.
+      // Since the buckets are only planned to be on wall-clock for Android P,
+      // condition is NOT supported for polled metrics.
+      PulledMetric polled_metric = 3;
+    }
+  }
+  optional ValueSource value = 2;
+
+  message TrackedAggregate {
+    // Must be an integer that is uniquely chosen so we can identify the metric
+    // on server. We will provide a tool on server to help generate this.
+    optional int32 metric_id = 1;
+
+    optional TrackedAggregateType type = 2;
+
+    // Alert if the value, when summed over the Counter's number_of_buckets
+    // most-recent bins, exceeds min_threshold or is below max_threshold. For
+    // Anomaly Detection.
+    message Alert {
+      message IncidentdDetails {
+        optional string
+            alert_name = 1;  // for humans and incidentd to identify this issue
+        repeated int32 incidentd_sections = 2;  // tells incidentd what to do if
+                                                // alert triggers
+      }
+      optional IncidentdDetails incidentd_details = 1;
+      optional int32 number_of_buckets = 2;
+      // NOTE: that we assume the aggregate is only int.
+      optional int64 trigger_if_gt = 3;    // min threshold
+      optional int64 trigger_if_lt = 4;       // max_threshold;
+      optional int32 refractory_period_secs = 5;  // alarm cannot fire a second
+                                                  // time until elapsed
+    }
+    repeated Alert alerts = 3;  // Support diff alert params for same aggregate.
+  }  // end TrackedAggregate
+  repeated TrackedAggregate tracked_aggregates = 3;
+
+  optional Condition condition = 4;
+
+  message Bucket {
+    // TODO: Consider switching to second granularity.
+    // In practice, this must be chosen from a pre-defined list. So that we have
+    // flexiblity, we don't hard-code this as an enum today.
+    optional int64 bucket_size_msec = 1;
+    optional int32 max_number_of_bits = 2; // Max bits per bucket.
+  }
+  optional Bucket bucket = 5;
+
+  message MiscellaneousEffect {
+    optional LineMatcher matcher = 1;  // When to trigger the effect
+
+    enum Effect {
+      STOP_ALL = 1;  // Needed for stop-all events, where nested start value is
+                     // forced to 0.
+    }
+    repeated Effect effects = 2;
+  }  // end MiscellaneousEffect
+  repeated MiscellaneousEffect misc_effects = 6;
+}  // end Counter
+
+// Alarm configs not tied to a particular Counter.
+message GlobalAlertParameters {
+  // No alarm can fire after any other alarm fired until this many seconds has
+  // elapsed.
+  optional int32 global_refractory_period_seconds = 1;
+}
+
+// The config defining all metrics to be captured.
+message StatsdConfig {
+  // Event matchers.
+  repeated EventMatcher event_matchers = 1;
+
+  // Aggregate counters.
+  repeated AggregateCounter aggregate_counters = 2;
+}
+
+/* Sample configurations start here:
+----Screen on time----
+AggregateCounter <
+  condition <
+    range <
+      start <
+        tag: SCREEN_ON
+        requirement <
+          key_matcher<
+            key: SCREEN_ON_VALUE
+          eq_bool: true
+      stop <
+        tag: SCREEN_ON
+        requirement <
+          key_matcher<
+            key: SCREEN_ON_VALUE
+          eq_bool: false
+  metric_id: # set on server
+  tracked_aggregates <
+    DURATION_SUM
+  (For brevity, omit the bucket options but they can also be set)
+
+----Screen off time----
+Should be like aboe but reversing start and stop
+
+----Log the screen change events----
+EventMatcher <
+  metric_id: # set on server
+  what <
+    tag: SCREEN_ON
+
+----Number of crashes (across system)----
+AggregateCounter <
+  metric_id: # set on server
+  tracked_aggregates <
+    VALUE_COUNT
+  value <
+    pushed_metric <
+      what <
+        tag: CRASH_TAG
+
+----Network Usage in bytes Per App While in Background----
+AggregateCounter <
+  metric_id: # set on server
+  slicer <
+    keys <
+      use_package_name: true
+  tracked_aggregates <
+    VALUE_SUM
+  value <
+    pulled_metric <
+      compute_diff: true
+      metric: Enum corresponding to network usage in bytes
+  condition <
+    range <
+      sliced: true
+      start <
+        tag: APP_FOREGROUND_TRANSITION (assume false means move to background)
+        requirement <
+          key_matcher<
+            key: APP_FOREGROUND_STATE
+          eq_bool: false
+      stop <
+        tag: APP_FOREGROUND_TRANSITION (assume false means move to background)
+        requirement <
+          key_matcher<
+            key: APP_FOREGROUND_STATE
+          eq_bool: true
+
+----Wakelock Acquire time per app and wakelock
+    while unplugged and screen off and in background process state----
+AggregateCounter <
+  metric_id: # set on server
+  slicer <
+    keys <
+      use_package_name: true
+    keys <
+      key: Key corresponding to wake_lock ID
+  tracked_aggregates <
+    DURATION_SUM
+  condition <
+    nested <
+      operation: AND
+      nested_conditions <
+        range <
+          start <
+            tag: PLUGGED_IN (assume false means uncharged)
+            requirement <
+              key_matcher<
+                key: PLUGGED_IN_STATE
+              eq_bool: false
+          stop <
+            tag: PLUGGED_IN (assume false means uncharged)
+            requirement <
+              key_matcher<
+                key: PLUGGED_IN_STATE
+              eq_bool: true
+      nested_conditions <
+        range <
+          start <
+            tag: SCREEN_ON
+            requirement <
+              key_matcher<
+                key: SCREEN_ON_STATE
+              eq_bool: false
+          stop <
+            tag: SCREEN_ON
+            requirement <
+              key_matcher<
+                key: SCREEN_ON_STATE
+              eq_bool: true
+      nested_conditions <
+        range <
+          start <
+            tag: PROCESS_CHANGE
+            requirement <
+              key_matcher<
+                key: PROCESS_STATE_VALUE
+              eq_int32: BACKGROUND_PROCESS
+          stop <
+            tag: PROCESS_CHANGE
+            nested <
+              operation: NOT
+              matcher< (This is an example of using the NOT to define stop)
+                requirement < (Note this requirement should match the start.)
+                  key_matcher<
+                    key: PROCESS_STATE_VALUE
+                  eq_int32: BACKGROUND_PROCESS
+          slice<
+              use_package_name: true
+
+
+----Number of crashes (per app) ----
+AggregateCounter <
+  metric_id: # set on server
+  slicer <
+    keys<
+      use_package_name: true
+  tracked_aggregates <
+    VALUE_COUNT
+  value <
+      pushed_metric <
+        what <
+          tag: CRASH_TAG
+
+---- Number of transitions to background (per app) ----
+AggregateCounter <
+  metric_id: # set on server
+  slicer <
+    keys<
+      use_package_name: true
+  tracked_aggregates <
+    VALUE_COUNT
+  value <
+      pushed_metric <
+        what <
+          tag: APP_FOREGROUND_TRANSITION
+          requirement<
+            key: APP_FOREGROUND_TRANSITION_STATE
+            eq_bool: false
+
+*/