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
+
+*/