summaryrefslogtreecommitdiff
path: root/cmds/statsd/src
diff options
context:
space:
mode:
Diffstat (limited to 'cmds/statsd/src')
-rw-r--r--cmds/statsd/src/FieldValue.cpp49
-rw-r--r--cmds/statsd/src/FieldValue.h64
-rw-r--r--cmds/statsd/src/HashableDimensionKey.cpp198
-rw-r--r--cmds/statsd/src/HashableDimensionKey.h102
-rw-r--r--cmds/statsd/src/StatsLogProcessor.cpp454
-rw-r--r--cmds/statsd/src/StatsLogProcessor.h96
-rw-r--r--cmds/statsd/src/StatsService.cpp733
-rw-r--r--cmds/statsd/src/StatsService.h145
-rw-r--r--cmds/statsd/src/annotations.h (renamed from cmds/statsd/src/external/PowerStatsPuller.h)27
-rw-r--r--cmds/statsd/src/anomaly/AlarmMonitor.cpp12
-rw-r--r--cmds/statsd/src/anomaly/AlarmMonitor.h19
-rw-r--r--cmds/statsd/src/anomaly/AlarmTracker.cpp1
-rw-r--r--cmds/statsd/src/anomaly/AlarmTracker.h3
-rw-r--r--cmds/statsd/src/anomaly/AnomalyTracker.cpp62
-rw-r--r--cmds/statsd/src/anomaly/AnomalyTracker.h12
-rw-r--r--cmds/statsd/src/atom_field_options.proto85
-rw-r--r--cmds/statsd/src/atoms.proto4093
-rw-r--r--cmds/statsd/src/condition/CombinationConditionTracker.cpp58
-rw-r--r--cmds/statsd/src/condition/CombinationConditionTracker.h15
-rw-r--r--cmds/statsd/src/condition/ConditionTracker.h41
-rw-r--r--cmds/statsd/src/condition/ConditionWizard.cpp20
-rw-r--r--cmds/statsd/src/condition/ConditionWizard.h10
-rw-r--r--cmds/statsd/src/condition/SimpleConditionTracker.cpp95
-rw-r--r--cmds/statsd/src/condition/SimpleConditionTracker.h15
-rw-r--r--cmds/statsd/src/condition/StateTracker.cpp232
-rw-r--r--cmds/statsd/src/condition/StateTracker.h131
-rw-r--r--cmds/statsd/src/config/ConfigManager.cpp117
-rw-r--r--cmds/statsd/src/config/ConfigManager.h41
-rw-r--r--cmds/statsd/src/experiment_ids.proto (renamed from cmds/statsd/src/external/CarStatsPuller.h)29
-rw-r--r--cmds/statsd/src/external/CarStatsPuller.cpp96
-rw-r--r--cmds/statsd/src/external/GpuStatsPuller.cpp155
-rw-r--r--cmds/statsd/src/external/Perfetto.h4
-rw-r--r--cmds/statsd/src/external/PowerStatsPuller.cpp170
-rw-r--r--cmds/statsd/src/external/PullResultReceiver.cpp (renamed from cmds/statsd/src/external/GpuStatsPuller.h)31
-rw-r--r--cmds/statsd/src/external/PullResultReceiver.h48
-rw-r--r--cmds/statsd/src/external/PullUidProvider.h (renamed from cmds/statsd/src/external/SubsystemSleepStatePuller.h)20
-rw-r--r--cmds/statsd/src/external/ResourceHealthManagerPuller.cpp123
-rw-r--r--cmds/statsd/src/external/StatsCallbackPuller.cpp89
-rw-r--r--cmds/statsd/src/external/StatsCallbackPuller.h18
-rw-r--r--cmds/statsd/src/external/StatsCompanionServicePuller.cpp78
-rw-r--r--cmds/statsd/src/external/StatsCompanionServicePuller.h40
-rw-r--r--cmds/statsd/src/external/StatsPuller.cpp48
-rw-r--r--cmds/statsd/src/external/StatsPuller.h37
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.cpp475
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.h128
-rw-r--r--cmds/statsd/src/external/SubsystemSleepStatePuller.cpp378
-rw-r--r--cmds/statsd/src/external/TrainInfoPuller.cpp20
-rw-r--r--cmds/statsd/src/external/puller_util.cpp65
-rw-r--r--cmds/statsd/src/external/puller_util.h3
-rw-r--r--cmds/statsd/src/guardrail/StatsdStats.cpp109
-rw-r--r--cmds/statsd/src/guardrail/StatsdStats.h81
-rw-r--r--cmds/statsd/src/logd/LogEvent.cpp752
-rw-r--r--cmds/statsd/src/logd/LogEvent.h278
-rw-r--r--cmds/statsd/src/main.cpp65
-rw-r--r--cmds/statsd/src/matchers/matcher_util.cpp23
-rw-r--r--cmds/statsd/src/metadata_util.cpp122
-rw-r--r--cmds/statsd/src/metadata_util.h (renamed from cmds/statsd/src/external/ResourceHealthManagerPuller.h)23
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.cpp92
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.h37
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.cpp480
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.h38
-rw-r--r--cmds/statsd/src/metrics/EventMetricProducer.cpp23
-rw-r--r--cmds/statsd/src/metrics/EventMetricProducer.h16
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.cpp129
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.h50
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.cpp155
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.h376
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.cpp198
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.h83
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.cpp540
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.h125
-rw-r--r--cmds/statsd/src/metrics/duration_helper/DurationTracker.h47
-rw-r--r--cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp65
-rw-r--r--cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h13
-rw-r--r--cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp194
-rw-r--r--cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h18
-rw-r--r--cmds/statsd/src/metrics/metrics_manager_util.cpp502
-rw-r--r--cmds/statsd/src/metrics/metrics_manager_util.h29
-rw-r--r--cmds/statsd/src/packages/UidMap.cpp8
-rw-r--r--cmds/statsd/src/packages/UidMap.h7
-rw-r--r--cmds/statsd/src/shell/ShellSubscriber.cpp301
-rw-r--r--cmds/statsd/src/shell/ShellSubscriber.h93
-rw-r--r--cmds/statsd/src/shell/shell_config.proto3
-rwxr-xr-xcmds/statsd/src/socket/StatsSocketListener.cpp23
-rw-r--r--cmds/statsd/src/state/StateListener.h54
-rw-r--r--cmds/statsd/src/state/StateManager.cpp112
-rw-r--r--cmds/statsd/src/state/StateManager.h103
-rw-r--r--cmds/statsd/src/state/StateTracker.cpp190
-rw-r--r--cmds/statsd/src/state/StateTracker.h94
-rw-r--r--cmds/statsd/src/stats_log.proto85
-rw-r--r--cmds/statsd/src/stats_log_util.cpp120
-rw-r--r--cmds/statsd/src/stats_log_util.h35
-rw-r--r--cmds/statsd/src/statscompanion_util.cpp20
-rw-r--r--cmds/statsd/src/statscompanion_util.h7
-rw-r--r--cmds/statsd/src/statsd_config.proto86
-rw-r--r--cmds/statsd/src/statsd_metadata.proto67
-rw-r--r--cmds/statsd/src/storage/StorageManager.cpp160
-rw-r--r--cmds/statsd/src/storage/StorageManager.h17
-rw-r--r--cmds/statsd/src/subscriber/IncidentdReporter.cpp48
-rw-r--r--cmds/statsd/src/subscriber/SubscriberReporter.cpp143
-rw-r--r--cmds/statsd/src/subscriber/SubscriberReporter.h55
-rw-r--r--cmds/statsd/src/utils/MultiConditionTrigger.cpp57
-rw-r--r--cmds/statsd/src/utils/MultiConditionTrigger.h55
103 files changed, 9957 insertions, 5634 deletions
diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp
index 320a32ab4648..c9ccfb93c86d 100644
--- a/cmds/statsd/src/FieldValue.cpp
+++ b/cmds/statsd/src/FieldValue.cpp
@@ -18,7 +18,6 @@
#include "Log.h"
#include "FieldValue.h"
#include "HashableDimensionKey.h"
-#include "atoms_info.h"
#include "math.h"
namespace android {
@@ -116,28 +115,13 @@ void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* ou
}
bool isAttributionUidField(const FieldValue& value) {
- int field = value.mField.getField() & 0xff007f;
- if (field == 0x10001 && value.mValue.getType() == INT) {
- return true;
- }
- return false;
+ return isAttributionUidField(value.mField, value.mValue);
}
int32_t getUidIfExists(const FieldValue& value) {
- bool isUid = false;
- // the field is uid field if the field is the uid field in attribution node or marked as
- // is_uid in atoms.proto
- if (isAttributionUidField(value)) {
- isUid = true;
- } else {
- auto it = android::util::AtomsInfo::kAtomsWithUidField.find(value.mField.getTag());
- if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
- int uidField = it->second; // uidField is the field number in proto
- isUid = value.mField.getDepth() == 0 && value.mField.getPosAtDepth(0) == uidField &&
- value.mValue.getType() == INT;
- }
- }
-
+ // the field is uid field if the field is the uid field in attribution node
+ // or annotated as such in the atom
+ bool isUid = isAttributionUidField(value) || isUidField(value);
return isUid ? value.mValue.int_value : -1;
}
@@ -149,6 +133,10 @@ bool isAttributionUidField(const Field& field, const Value& value) {
return false;
}
+bool isUidField(const FieldValue& fieldValue) {
+ return fieldValue.mAnnotations.isUidField();
+}
+
Value::Value(const Value& from) {
type = from.getType();
switch (type) {
@@ -438,6 +426,25 @@ bool equalDimensions(const std::vector<Matcher>& dimension_a,
return eq;
}
+bool subsetDimensions(const std::vector<Matcher>& dimension_a,
+ const std::vector<Matcher>& dimension_b) {
+ if (dimension_a.size() > dimension_b.size()) {
+ return false;
+ }
+ for (size_t i = 0; i < dimension_a.size(); ++i) {
+ bool found = false;
+ for (size_t j = 0; j < dimension_b.size(); ++j) {
+ if (dimension_a[i] == dimension_b[j]) {
+ found = true;
+ }
+ }
+ if (!found) {
+ return false;
+ }
+ }
+ return true;
+}
+
bool HasPositionANY(const FieldMatcher& matcher) {
if (matcher.has_position() && matcher.position() == Position::ANY) {
return true;
@@ -464,4 +471,4 @@ bool HasPositionALL(const FieldMatcher& matcher) {
} // namespace statsd
} // namespace os
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index 6729e052b5ee..fd86e3683970 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -16,6 +16,7 @@
#pragma once
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "annotations.h"
namespace android {
namespace os {
@@ -26,7 +27,6 @@ struct Matcher;
struct Field;
struct FieldValue;
-const int32_t kAttributionField = 1;
const int32_t kMaxLogDepth = 2;
const int32_t kLastBitMask = 0x80;
const int32_t kClearLastBitDeco = 0x7f;
@@ -180,6 +180,7 @@ public:
return false;
}
+
bool matches(const Matcher& that) const;
};
@@ -261,6 +262,11 @@ inline Matcher getSimpleMatcher(int32_t tag, size_t field) {
return Matcher(Field(tag, getSimpleField(field)), 0xff7f0000);
}
+inline Matcher getFirstUidMatcher(int32_t atomId) {
+ int32_t pos[] = {1, 1, 1};
+ return Matcher(Field(atomId, pos, 2), 0xff7f7f7f);
+}
+
/**
* A wrapper for a union type to contain multiple types of values.
*
@@ -352,6 +358,56 @@ struct Value {
Value& operator=(const Value& that);
};
+class Annotations {
+public:
+ Annotations() {
+ setNested(true); // Nested = true by default
+ }
+
+ // This enum stores where particular annotations can be found in the
+ // bitmask. Note that these pos do not correspond to annotation ids.
+ enum {
+ NESTED_POS = 0x0,
+ PRIMARY_POS = 0x1,
+ EXCLUSIVE_POS = 0x2,
+ UID_POS = 0x3
+ };
+
+ inline void setNested(bool nested) { setBitmaskAtPos(NESTED_POS, nested); }
+
+ inline void setPrimaryField(bool primary) { setBitmaskAtPos(PRIMARY_POS, primary); }
+
+ inline void setExclusiveState(bool exclusive) { setBitmaskAtPos(EXCLUSIVE_POS, exclusive); }
+
+ inline void setUidField(bool isUid) { setBitmaskAtPos(UID_POS, isUid); }
+
+ // Default value = false
+ inline bool isNested() const { return getValueFromBitmask(NESTED_POS); }
+
+ // Default value = false
+ inline bool isPrimaryField() const { return getValueFromBitmask(PRIMARY_POS); }
+
+ // Default value = false
+ inline bool isExclusiveState() const { return getValueFromBitmask(EXCLUSIVE_POS); }
+
+ // Default value = false
+ inline bool isUidField() const { return getValueFromBitmask(UID_POS); }
+
+private:
+ inline void setBitmaskAtPos(int pos, bool value) {
+ mBooleanBitmask &= ~(1 << pos); // clear
+ mBooleanBitmask |= (value << pos); // set
+ }
+
+ inline bool getValueFromBitmask(int pos) const {
+ return (mBooleanBitmask >> pos) & 0x1;
+ }
+
+ // This is a bitmask over all annotations stored in boolean form. Because
+ // there are only 4 booleans, just one byte is required.
+ uint8_t mBooleanBitmask = 0;
+};
+
/**
* Represents a log item, or a dimension item (They are essentially the same).
*/
@@ -379,6 +435,7 @@ struct FieldValue {
Field mField;
Value mValue;
+ Annotations mAnnotations;
};
bool HasPositionANY(const FieldMatcher& matcher);
@@ -392,9 +449,14 @@ int getUidIfExists(const FieldValue& value);
void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output);
bool isAttributionUidField(const Field& field, const Value& value);
+bool isUidField(const FieldValue& fieldValue);
bool equalDimensions(const std::vector<Matcher>& dimension_a,
const std::vector<Matcher>& dimension_b);
+
+// Returns true if dimension_a is a subset of dimension_b.
+bool subsetDimensions(const std::vector<Matcher>& dimension_a,
+ const std::vector<Matcher>& dimension_b);
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 05acef8b219d..eba66e0cb7b0 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -25,6 +25,100 @@ namespace statsd {
using std::string;
using std::vector;
+using android::base::StringPrintf;
+
+// These constants must be kept in sync with those in StatsDimensionsValue.java
+const static int STATS_DIMENSIONS_VALUE_STRING_TYPE = 2;
+const static int STATS_DIMENSIONS_VALUE_INT_TYPE = 3;
+const static int STATS_DIMENSIONS_VALUE_LONG_TYPE = 4;
+// const static int STATS_DIMENSIONS_VALUE_BOOL_TYPE = 5; (commented out because
+// unused -- statsd does not correctly support bool types)
+const static int STATS_DIMENSIONS_VALUE_FLOAT_TYPE = 6;
+const static int STATS_DIMENSIONS_VALUE_TUPLE_TYPE = 7;
+
+/**
+ * Recursive helper function that populates a parent StatsDimensionsValueParcel
+ * with children StatsDimensionsValueParcels.
+ *
+ * \param parent parcel that will be populated with children
+ * \param childDepth depth of children FieldValues
+ * \param childPrefix expected FieldValue prefix of children
+ * \param dims vector of FieldValues stored by HashableDimensionKey
+ * \param index position in dims to start reading children from
+ */
+static void populateStatsDimensionsValueParcelChildren(StatsDimensionsValueParcel& parent,
+ int childDepth, int childPrefix,
+ const vector<FieldValue>& dims,
+ size_t& index) {
+ if (childDepth > 2) {
+ ALOGE("Depth > 2 not supported by StatsDimensionsValueParcel.");
+ return;
+ }
+
+ while (index < dims.size()) {
+ const FieldValue& dim = dims[index];
+ int fieldDepth = dim.mField.getDepth();
+ int fieldPrefix = dim.mField.getPrefix(childDepth);
+
+ StatsDimensionsValueParcel child;
+ child.field = dim.mField.getPosAtDepth(childDepth);
+
+ if (fieldDepth == childDepth && fieldPrefix == childPrefix) {
+ switch (dim.mValue.getType()) {
+ case INT:
+ child.valueType = STATS_DIMENSIONS_VALUE_INT_TYPE;
+ child.intValue = dim.mValue.int_value;
+ break;
+ case LONG:
+ child.valueType = STATS_DIMENSIONS_VALUE_LONG_TYPE;
+ child.longValue = dim.mValue.long_value;
+ break;
+ case FLOAT:
+ child.valueType = STATS_DIMENSIONS_VALUE_FLOAT_TYPE;
+ child.floatValue = dim.mValue.float_value;
+ break;
+ case STRING:
+ child.valueType = STATS_DIMENSIONS_VALUE_STRING_TYPE;
+ child.stringValue = dim.mValue.str_value;
+ break;
+ default:
+ ALOGE("Encountered FieldValue with unsupported value type.");
+ break;
+ }
+ index++;
+ parent.tupleValue.push_back(child);
+ } else if (fieldDepth > childDepth && fieldPrefix == childPrefix) {
+ // This FieldValue is not a child of the current parent, but it is
+ // an indirect descendant. Thus, create a direct child of TUPLE_TYPE
+ // and recurse to parcel the indirect descendants.
+ child.valueType = STATS_DIMENSIONS_VALUE_TUPLE_TYPE;
+ populateStatsDimensionsValueParcelChildren(child, childDepth + 1,
+ dim.mField.getPrefix(childDepth + 1), dims,
+ index);
+ parent.tupleValue.push_back(child);
+ } else {
+ return;
+ }
+ }
+}
+
+StatsDimensionsValueParcel HashableDimensionKey::toStatsDimensionsValueParcel() const {
+ StatsDimensionsValueParcel root;
+ if (mValues.size() == 0) {
+ return root;
+ }
+
+ root.field = mValues[0].mField.getTag();
+ root.valueType = STATS_DIMENSIONS_VALUE_TUPLE_TYPE;
+
+ // Children of the root correspond to top-level (depth = 0) FieldValues.
+ int childDepth = 0;
+ int childPrefix = 0;
+ size_t index = 0;
+ populateStatsDimensionsValueParcelChildren(root, childDepth, childPrefix, mValues, index);
+
+ return root;
+}
android::hash_t hashDimension(const HashableDimensionKey& value) {
android::hash_t hash = 0;
@@ -57,6 +151,17 @@ android::hash_t hashDimension(const HashableDimensionKey& value) {
return JenkinsHashWhiten(hash);
}
+bool filterValues(const Matcher& matcherField, const vector<FieldValue>& values,
+ FieldValue* output) {
+ for (const auto& value : values) {
+ if (value.mField.matches(matcherField)) {
+ (*output) = value;
+ return true;
+ }
+ }
+ return false;
+}
+
bool filterValues(const vector<Matcher>& matcherFields, const vector<FieldValue>& values,
HashableDimensionKey* output) {
size_t num_matches = 0;
@@ -75,6 +180,23 @@ bool filterValues(const vector<Matcher>& matcherFields, const vector<FieldValue>
return num_matches > 0;
}
+bool filterPrimaryKey(const std::vector<FieldValue>& values, HashableDimensionKey* output) {
+ size_t num_matches = 0;
+ const int32_t simpleFieldMask = 0xff7f0000;
+ const int32_t attributionUidFieldMask = 0xff7f7f7f;
+ for (const auto& value : values) {
+ if (value.mAnnotations.isPrimaryField()) {
+ output->addValue(value);
+ output->mutableValue(num_matches)->mField.setTag(value.mField.getTag());
+ const int32_t mask =
+ isAttributionUidField(value) ? attributionUidFieldMask : simpleFieldMask;
+ output->mutableValue(num_matches)->mField.setField(value.mField.getField() & mask);
+ num_matches++;
+ }
+ }
+ return num_matches > 0;
+}
+
void filterGaugeValues(const std::vector<Matcher>& matcherFields,
const std::vector<FieldValue>& values, std::vector<FieldValue>* output) {
for (const auto& field : matcherFields) {
@@ -94,18 +216,78 @@ void getDimensionForCondition(const std::vector<FieldValue>& eventValues,
size_t count = conditionDimension->getValues().size();
if (count != links.conditionFields.size()) {
- // ALOGE("WTF condition link is bad");
return;
}
for (size_t i = 0; i < count; i++) {
conditionDimension->mutableValue(i)->mField.setField(
- links.conditionFields[i].mMatcher.getField());
+ links.conditionFields[i].mMatcher.getField());
conditionDimension->mutableValue(i)->mField.setTag(
- links.conditionFields[i].mMatcher.getTag());
+ links.conditionFields[i].mMatcher.getTag());
}
}
+void getDimensionForState(const std::vector<FieldValue>& eventValues, const Metric2State& link,
+ HashableDimensionKey* statePrimaryKey) {
+ // First, get the dimension from the event using the "what" fields from the
+ // MetricStateLinks.
+ filterValues(link.metricFields, eventValues, statePrimaryKey);
+
+ // Then check that the statePrimaryKey size equals the number of state fields
+ size_t count = statePrimaryKey->getValues().size();
+ if (count != link.stateFields.size()) {
+ return;
+ }
+
+ // For each dimension Value in the statePrimaryKey, set the field and tag
+ // using the state atom fields from MetricStateLinks.
+ for (size_t i = 0; i < count; i++) {
+ statePrimaryKey->mutableValue(i)->mField.setField(link.stateFields[i].mMatcher.getField());
+ statePrimaryKey->mutableValue(i)->mField.setTag(link.stateFields[i].mMatcher.getTag());
+ }
+}
+
+bool containsLinkedStateValues(const HashableDimensionKey& whatKey,
+ const HashableDimensionKey& primaryKey,
+ const vector<Metric2State>& stateLinks, const int32_t stateAtomId) {
+ if (whatKey.getValues().size() < primaryKey.getValues().size()) {
+ ALOGE("Contains linked values false: whatKey is too small");
+ return false;
+ }
+
+ for (const auto& primaryValue : primaryKey.getValues()) {
+ bool found = false;
+ for (const auto& whatValue : whatKey.getValues()) {
+ if (linked(stateLinks, stateAtomId, primaryValue.mField, whatValue.mField) &&
+ primaryValue.mValue == whatValue.mValue) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool linked(const vector<Metric2State>& stateLinks, const int32_t stateAtomId,
+ const Field& stateField, const Field& metricField) {
+ for (auto stateLink : stateLinks) {
+ if (stateLink.stateAtomId != stateAtomId) {
+ continue;
+ }
+
+ for (size_t i = 0; i < stateLink.stateFields.size(); i++) {
+ if (stateLink.stateFields[i].mMatcher == stateField &&
+ stateLink.metricFields[i].mMatcher == metricField) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
bool LessThan(const vector<FieldValue>& s1, const vector<FieldValue>& s2) {
if (s1.size() != s2.size()) {
return s1.size() < s2.size();
@@ -120,6 +302,10 @@ bool LessThan(const vector<FieldValue>& s1, const vector<FieldValue>& s2) {
return false;
}
+bool HashableDimensionKey::operator!=(const HashableDimensionKey& that) const {
+ return !((*this) == that);
+}
+
bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const {
if (mValues.size() != that.getValues().size()) {
return false;
@@ -173,11 +359,11 @@ string HashableDimensionKey::toString() const {
bool MetricDimensionKey::operator==(const MetricDimensionKey& that) const {
return mDimensionKeyInWhat == that.getDimensionKeyInWhat() &&
- mDimensionKeyInCondition == that.getDimensionKeyInCondition();
+ mStateValuesKey == that.getStateValuesKey();
};
string MetricDimensionKey::toString() const {
- return mDimensionKeyInWhat.toString() + mDimensionKeyInCondition.toString();
+ return mDimensionKeyInWhat.toString() + mStateValuesKey.toString();
}
bool MetricDimensionKey::operator<(const MetricDimensionKey& that) const {
@@ -187,7 +373,7 @@ bool MetricDimensionKey::operator<(const MetricDimensionKey& that) const {
return false;
}
- return mDimensionKeyInCondition < that.getDimensionKeyInCondition();
+ return mStateValuesKey < that.getStateValuesKey();
}
} // namespace statsd
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index 6f4941f717ee..bd011005a301 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -16,17 +16,18 @@
#pragma once
+#include <aidl/android/os/StatsDimensionsValueParcel.h>
#include <utils/JenkinsHash.h>
#include <vector>
-#include "FieldValue.h"
#include "android-base/stringprintf.h"
+#include "FieldValue.h"
#include "logd/LogEvent.h"
namespace android {
namespace os {
namespace statsd {
-using android::base::StringPrintf;
+using ::aidl::android::os::StatsDimensionsValueParcel;
struct Metric2Condition {
int64_t conditionId;
@@ -34,6 +35,12 @@ struct Metric2Condition {
std::vector<Matcher> conditionFields;
};
+struct Metric2State {
+ int32_t stateAtomId;
+ std::vector<Matcher> metricFields;
+ std::vector<Matcher> stateFields;
+};
+
class HashableDimensionKey {
public:
explicit HashableDimensionKey(const std::vector<FieldValue>& values) {
@@ -63,8 +70,12 @@ public:
return nullptr;
}
+ StatsDimensionsValueParcel toStatsDimensionsValueParcel() const;
+
std::string toString() const;
+ bool operator!=(const HashableDimensionKey& that) const;
+
bool operator==(const HashableDimensionKey& that) const;
bool operator<(const HashableDimensionKey& that) const;
@@ -76,17 +87,16 @@ private:
};
class MetricDimensionKey {
- public:
+public:
explicit MetricDimensionKey(const HashableDimensionKey& dimensionKeyInWhat,
- const HashableDimensionKey& dimensionKeyInCondition)
- : mDimensionKeyInWhat(dimensionKeyInWhat),
- mDimensionKeyInCondition(dimensionKeyInCondition) {};
+ const HashableDimensionKey& stateValuesKey)
+ : mDimensionKeyInWhat(dimensionKeyInWhat), mStateValuesKey(stateValuesKey){};
MetricDimensionKey(){};
MetricDimensionKey(const MetricDimensionKey& that)
: mDimensionKeyInWhat(that.getDimensionKeyInWhat()),
- mDimensionKeyInCondition(that.getDimensionKeyInCondition()) {};
+ mStateValuesKey(that.getStateValuesKey()){};
MetricDimensionKey& operator=(const MetricDimensionKey& from) = default;
@@ -96,30 +106,41 @@ class MetricDimensionKey {
return mDimensionKeyInWhat;
}
- inline const HashableDimensionKey& getDimensionKeyInCondition() const {
- return mDimensionKeyInCondition;
+ inline const HashableDimensionKey& getStateValuesKey() const {
+ return mStateValuesKey;
}
- inline void setDimensionKeyInCondition(const HashableDimensionKey& key) {
- mDimensionKeyInCondition = key;
+ inline HashableDimensionKey* getMutableStateValuesKey() {
+ return &mStateValuesKey;
}
- bool hasDimensionKeyInCondition() const {
- return mDimensionKeyInCondition.getValues().size() > 0;
+ inline void setStateValuesKey(const HashableDimensionKey& key) {
+ mStateValuesKey = key;
+ }
+
+ bool hasStateValuesKey() const {
+ return mStateValuesKey.getValues().size() > 0;
}
bool operator==(const MetricDimensionKey& that) const;
bool operator<(const MetricDimensionKey& that) const;
- private:
- HashableDimensionKey mDimensionKeyInWhat;
- HashableDimensionKey mDimensionKeyInCondition;
+private:
+ HashableDimensionKey mDimensionKeyInWhat;
+ HashableDimensionKey mStateValuesKey;
};
android::hash_t hashDimension(const HashableDimensionKey& key);
/**
+ * Returns true if a FieldValue field matches the matcher field.
+ * The value of the FieldValue is output.
+ */
+bool filterValues(const Matcher& matcherField, const std::vector<FieldValue>& values,
+ FieldValue* output);
+
+/**
* Creating HashableDimensionKeys from FieldValues using matcher.
*
* This function may make modifications to the Field if the matcher has Position=FIRST,LAST or ALL
@@ -133,6 +154,18 @@ bool filterValues(const std::vector<Matcher>& matcherFields, const std::vector<F
HashableDimensionKey* output);
/**
+ * Creating HashableDimensionKeys from State Primary Keys in FieldValues.
+ *
+ * This function may make modifications to the Field if the matcher has Position=FIRST,LAST or ALL
+ * in it. This is because: for example, when we create dimension from last uid in attribution chain,
+ * In one event, uid 1000 is at position 5 and it's the last
+ * In another event, uid 1000 is at position 6, and it's the last
+ * these 2 events should be mapped to the same dimension. So we will remove the original position
+ * from the dimension key for the uid field (by applying 0x80 bit mask).
+ */
+bool filterPrimaryKey(const std::vector<FieldValue>& values, HashableDimensionKey* output);
+
+/**
* Filter the values from FieldValues using the matchers.
*
* In contrast to the above function, this function will not do any modification to the original
@@ -145,6 +178,39 @@ void getDimensionForCondition(const std::vector<FieldValue>& eventValues,
const Metric2Condition& links,
HashableDimensionKey* conditionDimension);
+/**
+ * Get dimension values using metric's "what" fields and fill statePrimaryKey's
+ * mField information using "state" fields.
+ */
+void getDimensionForState(const std::vector<FieldValue>& eventValues, const Metric2State& link,
+ HashableDimensionKey* statePrimaryKey);
+
+/**
+ * Returns true if the primaryKey values are a subset of the whatKey values.
+ * The values from the primaryKey come from the state atom, so we need to
+ * check that a link exists between the state atom field and what atom field.
+ *
+ * Example:
+ * whatKey = [Atom: 10, {uid: 1005, wakelock_name: "compose"}]
+ * statePrimaryKey = [Atom: 27, {uid: 1005}]
+ * Returns true IF one of the Metric2State links Atom 10's uid to Atom 27's uid
+ *
+ * Example:
+ * whatKey = [Atom: 10, {uid: 1005, wakelock_name: "compose"}]
+ * statePrimaryKey = [Atom: 59, {uid: 1005, package_name: "system"}]
+ * Returns false
+ */
+bool containsLinkedStateValues(const HashableDimensionKey& whatKey,
+ const HashableDimensionKey& primaryKey,
+ const std::vector<Metric2State>& stateLinks,
+ const int32_t stateAtomId);
+
+/**
+ * Returns true if there is a Metric2State link that links the stateField and
+ * the metricField (they are equal fields from different atoms).
+ */
+bool linked(const std::vector<Metric2State>& stateLinks, const int32_t stateAtomId,
+ const Field& stateField, const Field& metricField);
} // namespace statsd
} // namespace os
} // namespace android
@@ -165,8 +231,8 @@ template <>
struct hash<MetricDimensionKey> {
std::size_t operator()(const MetricDimensionKey& key) const {
android::hash_t hash = hashDimension(key.getDimensionKeyInWhat());
- hash = android::JenkinsHashMix(hash, hashDimension(key.getDimensionKeyInCondition()));
+ hash = android::JenkinsHashMix(hash, hashDimension(key.getStateValuesKey()));
return android::JenkinsHashWhiten(hash);
}
};
-} // namespace std \ No newline at end of file
+} // namespace std
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index a730a0de6b88..e7b32c56551a 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -20,16 +20,20 @@
#include "StatsLogProcessor.h"
#include <android-base/file.h>
+#include <cutils/multiuser.h>
#include <frameworks/base/cmds/statsd/src/active_config_list.pb.h>
+#include <frameworks/base/cmds/statsd/src/experiment_ids.pb.h>
#include "android-base/stringprintf.h"
-#include "atoms_info.h"
#include "external/StatsPullerManager.h"
#include "guardrail/StatsdStats.h"
+#include "logd/LogEvent.h"
#include "metrics/CountMetricProducer.h"
+#include "StatsService.h"
+#include "state/StateManager.h"
#include "stats_log_util.h"
#include "stats_util.h"
-#include "statslog.h"
+#include "statslog_statsd.h"
#include "storage/StorageManager.h"
using namespace android;
@@ -67,9 +71,14 @@ const int FIELD_ID_STRINGS = 9;
// for ActiveConfigList
const int FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG = 1;
+// for permissions checks
+constexpr const char* kPermissionDump = "android.permission.DUMP";
+constexpr const char* kPermissionUsage = "android.permission.PACKAGE_USAGE_STATS";
+
#define NS_PER_HOUR 3600 * NS_PER_SEC
#define STATS_ACTIVE_METRIC_DIR "/data/misc/stats-active-metric"
+#define STATS_METADATA_DIR "/data/misc/stats-metadata"
// Cool down period for writing data to disk to avoid overwriting files.
#define WRITE_DATA_COOL_DOWN_SEC 5
@@ -92,6 +101,7 @@ StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
mLargestTimestampSeen(0),
mLastTimestampSeen(0) {
mPullerManager->ForceClearPullerCache();
+ StateManager::getInstance().updateLogSources(uidMap);
}
StatsLogProcessor::~StatsLogProcessor() {
@@ -128,38 +138,22 @@ void StatsLogProcessor::onPeriodicAlarmFired(
}
}
-void updateUid(Value* value, int hostUid) {
- int uid = value->int_value;
- if (uid != hostUid) {
- value->setInt(hostUid);
- }
-}
-
void StatsLogProcessor::mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const {
- if (android::util::AtomsInfo::kAtomsWithAttributionChain.find(event->GetTagId()) !=
- android::util::AtomsInfo::kAtomsWithAttributionChain.end()) {
- for (auto& value : *(event->getMutableValues())) {
- if (value.mField.getPosAtDepth(0) > kAttributionField) {
- break;
- }
- if (isAttributionUidField(value)) {
- const int hostUid = mUidMap->getHostUidOrSelf(value.mValue.int_value);
- updateUid(&value.mValue, hostUid);
+ if (std::pair<int, int> indexRange; event->hasAttributionChain(&indexRange)) {
+ vector<FieldValue>* const fieldValues = event->getMutableValues();
+ for (int i = indexRange.first; i <= indexRange.second; i++) {
+ FieldValue& fieldValue = fieldValues->at(i);
+ if (isAttributionUidField(fieldValue)) {
+ const int hostUid = mUidMap->getHostUidOrSelf(fieldValue.mValue.int_value);
+ fieldValue.mValue.setInt(hostUid);
}
}
} else {
- auto it = android::util::AtomsInfo::kAtomsWithUidField.find(event->GetTagId());
- if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
- int uidField = it->second; // uidField is the field number in proto,
- // starting from 1
- if (uidField > 0 && (int)event->getValues().size() >= uidField &&
- (event->getValues())[uidField - 1].mValue.getType() == INT) {
- Value& value = (*event->getMutableValues())[uidField - 1].mValue;
- const int hostUid = mUidMap->getHostUidOrSelf(value.int_value);
- updateUid(&value, hostUid);
- } else {
- ALOGE("Malformed log, uid not found. %s", event->ToString().c_str());
- }
+ int uidFieldIndex = event->getUidFieldIndex();
+ if (uidFieldIndex != -1) {
+ Value& value = (*event->getMutableValues())[uidFieldIndex].mValue;
+ const int hostUid = mUidMap->getHostUidOrSelf(value.int_value);
+ value.setInt(hostUid);
}
}
}
@@ -180,6 +174,200 @@ void StatsLogProcessor::onIsolatedUidChangedEventLocked(const LogEvent& event) {
}
}
+void StatsLogProcessor::onBinaryPushStateChangedEventLocked(LogEvent* event) {
+ pid_t pid = event->GetPid();
+ uid_t uid = event->GetUid();
+ if (!checkPermissionForIds(kPermissionDump, pid, uid) ||
+ !checkPermissionForIds(kPermissionUsage, pid, uid)) {
+ return;
+ }
+ // The Get* functions don't modify the status on success, they only write in
+ // failure statuses, so we can use one status variable for all calls then
+ // check if it is no longer NO_ERROR.
+ status_t err = NO_ERROR;
+ InstallTrainInfo trainInfo;
+ trainInfo.trainName = string(event->GetString(1 /*train name field id*/, &err));
+ trainInfo.trainVersionCode = event->GetLong(2 /*train version field id*/, &err);
+ trainInfo.requiresStaging = event->GetBool(3 /*requires staging field id*/, &err);
+ trainInfo.rollbackEnabled = event->GetBool(4 /*rollback enabled field id*/, &err);
+ trainInfo.requiresLowLatencyMonitor =
+ event->GetBool(5 /*requires low latency monitor field id*/, &err);
+ trainInfo.status = int32_t(event->GetLong(6 /*state field id*/, &err));
+ std::vector<uint8_t> trainExperimentIdBytes =
+ event->GetStorage(7 /*experiment ids field id*/, &err);
+ bool is_rollback = event->GetBool(10 /*is rollback field id*/, &err);
+
+ if (err != NO_ERROR) {
+ ALOGE("Failed to parse fields in binary push state changed log event");
+ return;
+ }
+ ExperimentIds trainExperimentIds;
+ if (!trainExperimentIds.ParseFromArray(trainExperimentIdBytes.data(),
+ trainExperimentIdBytes.size())) {
+ ALOGE("Failed to parse experimentids in binary push state changed.");
+ return;
+ }
+ trainInfo.experimentIds = {trainExperimentIds.experiment_id().begin(),
+ trainExperimentIds.experiment_id().end()};
+
+ // Update the train info on disk and get any data the logevent is missing.
+ getAndUpdateTrainInfoOnDisk(is_rollback, &trainInfo);
+
+ std::vector<uint8_t> trainExperimentIdProto;
+ writeExperimentIdsToProto(trainInfo.experimentIds, &trainExperimentIdProto);
+ int32_t userId = multiuser_get_user_id(uid);
+
+ event->updateValue(2 /*train version field id*/, trainInfo.trainVersionCode, LONG);
+ event->updateValue(7 /*experiment ids field id*/, trainExperimentIdProto, STORAGE);
+ event->updateValue(8 /*user id field id*/, userId, INT);
+
+ // If this event is a rollback event, then the following bits in the event
+ // are invalid and we will need to update them with the values we pulled
+ // from disk.
+ if (is_rollback) {
+ int bit = trainInfo.requiresStaging ? 1 : 0;
+ event->updateValue(3 /*requires staging field id*/, bit, INT);
+ bit = trainInfo.rollbackEnabled ? 1 : 0;
+ event->updateValue(4 /*rollback enabled field id*/, bit, INT);
+ bit = trainInfo.requiresLowLatencyMonitor ? 1 : 0;
+ event->updateValue(5 /*requires low latency monitor field id*/, bit, INT);
+ }
+}
+
+void StatsLogProcessor::getAndUpdateTrainInfoOnDisk(bool is_rollback,
+ InstallTrainInfo* trainInfo) {
+ // If the train name is empty, we don't know which train to attribute the
+ // event to, so return early.
+ if (trainInfo->trainName.empty()) {
+ return;
+ }
+ bool readTrainInfoSuccess = false;
+ InstallTrainInfo trainInfoOnDisk;
+ readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfo->trainName, trainInfoOnDisk);
+
+ bool resetExperimentIds = false;
+ if (readTrainInfoSuccess) {
+ // Keep the old train version if we received an empty version.
+ if (trainInfo->trainVersionCode == -1) {
+ trainInfo->trainVersionCode = trainInfoOnDisk.trainVersionCode;
+ } else if (trainInfo->trainVersionCode != trainInfoOnDisk.trainVersionCode) {
+ // Reset experiment ids if we receive a new non-empty train version.
+ resetExperimentIds = true;
+ }
+
+ // Reset if we received a different experiment id.
+ if (!trainInfo->experimentIds.empty() &&
+ (trainInfoOnDisk.experimentIds.empty() ||
+ trainInfo->experimentIds.at(0) != trainInfoOnDisk.experimentIds[0])) {
+ resetExperimentIds = true;
+ }
+ }
+
+ // Find the right experiment IDs
+ if ((!resetExperimentIds || is_rollback) && readTrainInfoSuccess) {
+ trainInfo->experimentIds = trainInfoOnDisk.experimentIds;
+ }
+
+ if (!trainInfo->experimentIds.empty()) {
+ int64_t firstId = trainInfo->experimentIds.at(0);
+ auto& ids = trainInfo->experimentIds;
+ switch (trainInfo->status) {
+ case android::os::statsd::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALL_SUCCESS:
+ if (find(ids.begin(), ids.end(), firstId + 1) == ids.end()) {
+ ids.push_back(firstId + 1);
+ }
+ break;
+ case android::os::statsd::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_INITIATED:
+ if (find(ids.begin(), ids.end(), firstId + 2) == ids.end()) {
+ ids.push_back(firstId + 2);
+ }
+ break;
+ case android::os::statsd::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_SUCCESS:
+ if (find(ids.begin(), ids.end(), firstId + 3) == ids.end()) {
+ ids.push_back(firstId + 3);
+ }
+ break;
+ }
+ }
+
+ // If this event is a rollback event, the following fields are invalid and
+ // need to be replaced by the fields stored to disk.
+ if (is_rollback) {
+ trainInfo->requiresStaging = trainInfoOnDisk.requiresStaging;
+ trainInfo->rollbackEnabled = trainInfoOnDisk.rollbackEnabled;
+ trainInfo->requiresLowLatencyMonitor = trainInfoOnDisk.requiresLowLatencyMonitor;
+ }
+
+ StorageManager::writeTrainInfo(*trainInfo);
+}
+
+void StatsLogProcessor::onWatchdogRollbackOccurredLocked(LogEvent* event) {
+ pid_t pid = event->GetPid();
+ uid_t uid = event->GetUid();
+ if (!checkPermissionForIds(kPermissionDump, pid, uid) ||
+ !checkPermissionForIds(kPermissionUsage, pid, uid)) {
+ return;
+ }
+ // The Get* functions don't modify the status on success, they only write in
+ // failure statuses, so we can use one status variable for all calls then
+ // check if it is no longer NO_ERROR.
+ status_t err = NO_ERROR;
+ int32_t rollbackType = int32_t(event->GetInt(1 /*rollback type field id*/, &err));
+ string packageName = string(event->GetString(2 /*package name field id*/, &err));
+
+ if (err != NO_ERROR) {
+ ALOGE("Failed to parse fields in watchdog rollback occurred log event");
+ return;
+ }
+
+ vector<int64_t> experimentIds =
+ processWatchdogRollbackOccurred(rollbackType, packageName);
+ vector<uint8_t> experimentIdProto;
+ writeExperimentIdsToProto(experimentIds, &experimentIdProto);
+
+ event->updateValue(6 /*experiment ids field id*/, experimentIdProto, STORAGE);
+}
+
+vector<int64_t> StatsLogProcessor::processWatchdogRollbackOccurred(const int32_t rollbackTypeIn,
+ const string& packageNameIn) {
+ // If the package name is empty, we can't attribute it to any train, so
+ // return early.
+ if (packageNameIn.empty()) {
+ return vector<int64_t>();
+ }
+ bool readTrainInfoSuccess = false;
+ InstallTrainInfo trainInfoOnDisk;
+ // We use the package name of the event as the train name.
+ readTrainInfoSuccess = StorageManager::readTrainInfo(packageNameIn, trainInfoOnDisk);
+
+ if (!readTrainInfoSuccess) {
+ return vector<int64_t>();
+ }
+
+ if (trainInfoOnDisk.experimentIds.empty()) {
+ return vector<int64_t>();
+ }
+
+ int64_t firstId = trainInfoOnDisk.experimentIds[0];
+ auto& ids = trainInfoOnDisk.experimentIds;
+ switch (rollbackTypeIn) {
+ case android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE:
+ if (find(ids.begin(), ids.end(), firstId + 4) == ids.end()) {
+ ids.push_back(firstId + 4);
+ }
+ StorageManager::writeTrainInfo(trainInfoOnDisk);
+ break;
+ case android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS:
+ if (find(ids.begin(), ids.end(), firstId + 5) == ids.end()) {
+ ids.push_back(firstId + 5);
+ }
+ StorageManager::writeTrainInfo(trainInfoOnDisk);
+ break;
+ }
+
+ return trainInfoOnDisk.experimentIds;
+}
+
void StatsLogProcessor::resetConfigs() {
std::lock_guard<std::mutex> lock(mMetricsMutex);
resetConfigsLocked(getElapsedRealtimeNs());
@@ -194,26 +382,51 @@ void StatsLogProcessor::resetConfigsLocked(const int64_t timestampNs) {
}
void StatsLogProcessor::OnLogEvent(LogEvent* event) {
+ OnLogEvent(event, getElapsedRealtimeNs());
+}
+
+void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
+ // Tell StatsdStats about new event
+ const int64_t eventElapsedTimeNs = event->GetElapsedTimestampNs();
+ int atomId = event->GetTagId();
+ StatsdStats::getInstance().noteAtomLogged(atomId, eventElapsedTimeNs / NS_PER_SEC);
+ if (!event->isValid()) {
+ StatsdStats::getInstance().noteAtomError(atomId);
+ return;
+ }
+
+ // Hard-coded logic to update train info on disk and fill in any information
+ // this log event may be missing.
+ if (atomId == android::os::statsd::util::BINARY_PUSH_STATE_CHANGED) {
+ onBinaryPushStateChangedEventLocked(event);
+ }
+
+ // Hard-coded logic to update experiment ids on disk for certain rollback
+ // types and fill the rollback atom with experiment ids
+ if (atomId == android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED) {
+ onWatchdogRollbackOccurredLocked(event);
+ }
+
#ifdef VERY_VERBOSE_PRINTING
if (mPrintAllLogs) {
ALOGI("%s", event->ToString().c_str());
}
#endif
- const int64_t currentTimestampNs = event->GetElapsedTimestampNs();
-
- resetIfConfigTtlExpiredLocked(currentTimestampNs);
-
- StatsdStats::getInstance().noteAtomLogged(
- event->GetTagId(), event->GetElapsedTimestampNs() / NS_PER_SEC);
+ resetIfConfigTtlExpiredLocked(eventElapsedTimeNs);
// Hard-coded logic to update the isolated uid's in the uid-map.
// The field numbers need to be currently updated by hand with atoms.proto
- if (event->GetTagId() == android::util::ISOLATED_UID_CHANGED) {
+ if (atomId == android::os::statsd::util::ISOLATED_UID_CHANGED) {
onIsolatedUidChangedEventLocked(*event);
+ } else {
+ // Map the isolated uid to host uid if necessary.
+ mapIsolatedUidToHostUidIfNecessaryLocked(event);
}
+ StateManager::getInstance().onLogEvent(*event);
+
if (mMetricsManagers.empty()) {
return;
}
@@ -224,12 +437,6 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) {
mLastPullerCacheClearTimeSec = curTimeSec;
}
-
- if (event->GetTagId() != android::util::ISOLATED_UID_CHANGED) {
- // Map the isolated uid to host uid if necessary.
- mapIsolatedUidToHostUidIfNecessaryLocked(event);
- }
-
std::unordered_set<int> uidsWithActiveConfigsChanged;
std::unordered_map<int, std::vector<int64_t>> activeConfigsPerUid;
// pass the event to metrics managers.
@@ -256,15 +463,16 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) {
uidsWithActiveConfigsChanged.insert(uid);
StatsdStats::getInstance().noteActiveStatusChanged(pair.first, isCurActive);
}
- flushIfNecessaryLocked(event->GetElapsedTimestampNs(), pair.first, *(pair.second));
+ flushIfNecessaryLocked(pair.first, *(pair.second));
}
+ // Don't use the event timestamp for the guardrail.
for (int uid : uidsWithActiveConfigsChanged) {
// Send broadcast so that receivers can pull data.
auto lastBroadcastTime = mLastActivationBroadcastTimes.find(uid);
if (lastBroadcastTime != mLastActivationBroadcastTimes.end()) {
- if (currentTimestampNs - lastBroadcastTime->second <
- StatsdStats::kMinActivationBroadcastPeriodNs) {
+ if (elapsedRealtimeNs - lastBroadcastTime->second <
+ StatsdStats::kMinActivationBroadcastPeriodNs) {
StatsdStats::getInstance().noteActivationBroadcastGuardrailHit(uid);
VLOG("StatsD would've sent an activation broadcast but the rate limit stopped us.");
return;
@@ -274,13 +482,13 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) {
if (activeConfigs != activeConfigsPerUid.end()) {
if (mSendActivationBroadcast(uid, activeConfigs->second)) {
VLOG("StatsD sent activation notice for uid %d", uid);
- mLastActivationBroadcastTimes[uid] = currentTimestampNs;
+ mLastActivationBroadcastTimes[uid] = elapsedRealtimeNs;
}
} else {
std::vector<int64_t> emptyActiveConfigs;
if (mSendActivationBroadcast(uid, emptyActiveConfigs)) {
VLOG("StatsD sent EMPTY activation notice for uid %d", uid);
- mLastActivationBroadcastTimes[uid] = currentTimestampNs;
+ mLastActivationBroadcastTimes[uid] = elapsedRealtimeNs;
}
}
}
@@ -314,13 +522,16 @@ void StatsLogProcessor::OnConfigUpdatedLocked(
new MetricsManager(key, config, mTimeBaseNs, timestampNs, mUidMap, mPullerManager,
mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
if (newMetricsManager->isConfigValid()) {
+ newMetricsManager->init();
mUidMap->OnConfigUpdated(key);
newMetricsManager->refreshTtl(timestampNs);
mMetricsManagers[key] = newMetricsManager;
VLOG("StatsdConfig valid");
} else {
// If there is any error in the config, don't use it.
+ // Remove any existing config with the same key.
ALOGE("StatsdConfig NOT valid");
+ mMetricsManagers.erase(key);
}
}
@@ -537,22 +748,23 @@ void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
}
}
-void StatsLogProcessor::flushIfNecessaryLocked(
- int64_t timestampNs, const ConfigKey& key, MetricsManager& metricsManager) {
+void StatsLogProcessor::flushIfNecessaryLocked(const ConfigKey& key,
+ MetricsManager& metricsManager) {
+ int64_t elapsedRealtimeNs = getElapsedRealtimeNs();
auto lastCheckTime = mLastByteSizeTimes.find(key);
if (lastCheckTime != mLastByteSizeTimes.end()) {
- if (timestampNs - lastCheckTime->second < StatsdStats::kMinByteSizeCheckPeriodNs) {
+ if (elapsedRealtimeNs - lastCheckTime->second < StatsdStats::kMinByteSizeCheckPeriodNs) {
return;
}
}
// We suspect that the byteSize() computation is expensive, so we set a rate limit.
size_t totalBytes = metricsManager.byteSize();
- mLastByteSizeTimes[key] = timestampNs;
+ mLastByteSizeTimes[key] = elapsedRealtimeNs;
bool requestDump = false;
- if (totalBytes >
- StatsdStats::kMaxMetricsBytesPerConfig) { // Too late. We need to start clearing data.
- metricsManager.dropData(timestampNs);
+ if (totalBytes > StatsdStats::kMaxMetricsBytesPerConfig) {
+ // Too late. We need to start clearing data.
+ metricsManager.dropData(elapsedRealtimeNs);
StatsdStats::getInstance().noteDataDropped(key, totalBytes);
VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str());
} else if ((totalBytes > StatsdStats::kBytesPerConfigTriggerGetData) ||
@@ -567,7 +779,8 @@ void StatsLogProcessor::flushIfNecessaryLocked(
// Send broadcast so that receivers can pull data.
auto lastBroadcastTime = mLastBroadcastTimes.find(key);
if (lastBroadcastTime != mLastBroadcastTimes.end()) {
- if (timestampNs - lastBroadcastTime->second < StatsdStats::kMinBroadcastPeriodNs) {
+ if (elapsedRealtimeNs - lastBroadcastTime->second <
+ StatsdStats::kMinBroadcastPeriodNs) {
VLOG("StatsD would've sent a broadcast but the rate limit stopped us.");
return;
}
@@ -575,7 +788,7 @@ void StatsLogProcessor::flushIfNecessaryLocked(
if (mSendBroadcast(key)) {
mOnDiskDataConfigs.erase(key);
VLOG("StatsD triggered data fetch for %s", key.ToString().c_str());
- mLastBroadcastTimes[key] = timestampNs;
+ mLastBroadcastTimes[key] = elapsedRealtimeNs;
StatsdStats::getInstance().noteBroadcastSent(key);
}
}
@@ -627,6 +840,110 @@ void StatsLogProcessor::SaveActiveConfigsToDisk(int64_t currentTimeNs) {
proto.flush(fd.get());
}
+void StatsLogProcessor::SaveMetadataToDisk(int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs) {
+ std::lock_guard<std::mutex> lock(mMetricsMutex);
+ // Do not write to disk if we already have in the last few seconds.
+ if (static_cast<unsigned long long> (systemElapsedTimeNs) <
+ mLastMetadataWriteNs + WRITE_DATA_COOL_DOWN_SEC * NS_PER_SEC) {
+ ALOGI("Statsd skipping writing metadata to disk. Already wrote data in last %d seconds",
+ WRITE_DATA_COOL_DOWN_SEC);
+ return;
+ }
+ mLastMetadataWriteNs = systemElapsedTimeNs;
+
+ metadata::StatsMetadataList metadataList;
+ WriteMetadataToProtoLocked(
+ currentWallClockTimeNs, systemElapsedTimeNs, &metadataList);
+
+ string file_name = StringPrintf("%s/metadata", STATS_METADATA_DIR);
+ StorageManager::deleteFile(file_name.c_str());
+
+ if (metadataList.stats_metadata_size() == 0) {
+ // Skip the write if we have nothing to write.
+ return;
+ }
+
+ std::string data;
+ metadataList.SerializeToString(&data);
+ StorageManager::writeFile(file_name.c_str(), data.c_str(), data.size());
+}
+
+void StatsLogProcessor::WriteMetadataToProto(int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs,
+ metadata::StatsMetadataList* metadataList) {
+ std::lock_guard<std::mutex> lock(mMetricsMutex);
+ WriteMetadataToProtoLocked(currentWallClockTimeNs, systemElapsedTimeNs, metadataList);
+}
+
+void StatsLogProcessor::WriteMetadataToProtoLocked(int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs,
+ metadata::StatsMetadataList* metadataList) {
+ for (const auto& pair : mMetricsManagers) {
+ const sp<MetricsManager>& metricsManager = pair.second;
+ metadata::StatsMetadata* statsMetadata = metadataList->add_stats_metadata();
+ bool metadataWritten = metricsManager->writeMetadataToProto(currentWallClockTimeNs,
+ systemElapsedTimeNs, statsMetadata);
+ if (!metadataWritten) {
+ metadataList->mutable_stats_metadata()->RemoveLast();
+ }
+ }
+}
+
+void StatsLogProcessor::LoadMetadataFromDisk(int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs) {
+ std::lock_guard<std::mutex> lock(mMetricsMutex);
+ string file_name = StringPrintf("%s/metadata", STATS_METADATA_DIR);
+ int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
+ if (-1 == fd) {
+ VLOG("Attempt to read %s but failed", file_name.c_str());
+ StorageManager::deleteFile(file_name.c_str());
+ return;
+ }
+ string content;
+ if (!android::base::ReadFdToString(fd, &content)) {
+ ALOGE("Attempt to read %s but failed", file_name.c_str());
+ close(fd);
+ StorageManager::deleteFile(file_name.c_str());
+ return;
+ }
+
+ close(fd);
+
+ metadata::StatsMetadataList statsMetadataList;
+ if (!statsMetadataList.ParseFromString(content)) {
+ ALOGE("Attempt to read %s but failed; failed to metadata", file_name.c_str());
+ StorageManager::deleteFile(file_name.c_str());
+ return;
+ }
+ SetMetadataStateLocked(statsMetadataList, currentWallClockTimeNs, systemElapsedTimeNs);
+ StorageManager::deleteFile(file_name.c_str());
+}
+
+void StatsLogProcessor::SetMetadataState(const metadata::StatsMetadataList& statsMetadataList,
+ int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs) {
+ std::lock_guard<std::mutex> lock(mMetricsMutex);
+ SetMetadataStateLocked(statsMetadataList, currentWallClockTimeNs, systemElapsedTimeNs);
+}
+
+void StatsLogProcessor::SetMetadataStateLocked(
+ const metadata::StatsMetadataList& statsMetadataList,
+ int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs) {
+ for (const metadata::StatsMetadata& metadata : statsMetadataList.stats_metadata()) {
+ ConfigKey key(metadata.config_key().uid(), metadata.config_key().config_id());
+ auto it = mMetricsManagers.find(key);
+ if (it == mMetricsManagers.end()) {
+ ALOGE("No config found for configKey %s", key.ToString().c_str());
+ continue;
+ }
+ VLOG("Setting metadata %s", key.ToString().c_str());
+ it->second->loadMetadata(metadata, currentWallClockTimeNs, systemElapsedTimeNs);
+ }
+ VLOG("Successfully loaded %d metadata.", statsMetadataList.stats_metadata_size());
+}
+
void StatsLogProcessor::WriteActiveConfigsToProtoOutputStream(
int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
@@ -736,8 +1053,9 @@ int64_t StatsLogProcessor::getLastReportTimeNs(const ConfigKey& key) {
void StatsLogProcessor::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk,
const int uid, const int64_t version) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
- ALOGW("Received app upgrade");
- for (auto it : mMetricsManagers) {
+ VLOG("Received app upgrade");
+ StateManager::getInstance().notifyAppChanged(apk, mUidMap);
+ for (const auto& it : mMetricsManagers) {
it.second->notifyAppUpgrade(eventTimeNs, apk, uid, version);
}
}
@@ -745,20 +1063,30 @@ void StatsLogProcessor::notifyAppUpgrade(const int64_t& eventTimeNs, const strin
void StatsLogProcessor::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk,
const int uid) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
- ALOGW("Received app removed");
- for (auto it : mMetricsManagers) {
+ VLOG("Received app removed");
+ StateManager::getInstance().notifyAppChanged(apk, mUidMap);
+ for (const auto& it : mMetricsManagers) {
it.second->notifyAppRemoved(eventTimeNs, apk, uid);
}
}
void StatsLogProcessor::onUidMapReceived(const int64_t& eventTimeNs) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
- ALOGW("Received uid map");
- for (auto it : mMetricsManagers) {
+ VLOG("Received uid map");
+ StateManager::getInstance().updateLogSources(mUidMap);
+ for (const auto& it : mMetricsManagers) {
it.second->onUidMapReceived(eventTimeNs);
}
}
+void StatsLogProcessor::onStatsdInitCompleted(const int64_t& elapsedTimeNs) {
+ std::lock_guard<std::mutex> lock(mMetricsMutex);
+ VLOG("Received boot completed signal");
+ for (const auto& it : mMetricsManagers) {
+ it.second->onStatsdInitCompleted(elapsedTimeNs);
+ }
+}
+
void StatsLogProcessor::noteOnDiskData(const ConfigKey& key) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
mOnDiskDataConfigs.insert(key);
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 0d2b33ee0ce1..23f2584655b0 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -18,11 +18,13 @@
#include <gtest/gtest_prod.h>
#include "config/ConfigListener.h"
+#include "logd/LogEvent.h"
#include "metrics/MetricsManager.h"
#include "packages/UidMap.h"
#include "external/StatsPullerManager.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h"
#include <stdio.h>
#include <unordered_map>
@@ -88,6 +90,23 @@ public:
/* Load configs containing metrics with active activations from disk. */
void LoadActiveConfigsFromDisk();
+ /* Persist metadata for configs and metrics to disk. */
+ void SaveMetadataToDisk(int64_t currentWallClockTimeNs, int64_t systemElapsedTimeNs);
+
+ /* Writes the statsd metadata for all configs and metrics to StatsMetadataList. */
+ void WriteMetadataToProto(int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs,
+ metadata::StatsMetadataList* metadataList);
+
+ /* Load stats metadata for configs and metrics from disk. */
+ void LoadMetadataFromDisk(int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs);
+
+ /* Sets the metadata for all configs and metrics */
+ void SetMetadataState(const metadata::StatsMetadataList& statsMetadataList,
+ int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs);
+
/* Sets the active status/ttl for all configs and metrics to the status in ActiveConfigList. */
void SetConfigsActiveState(const ActiveConfigList& activeConfigList, int64_t currentTimeNs);
@@ -101,6 +120,11 @@ public:
/* Notify all MetricsManagers of uid map snapshots received */
void onUidMapReceived(const int64_t& eventTimeNs) override;
+ /* Notify all metrics managers of boot completed
+ * This will force a bucket split when the boot is finished.
+ */
+ void onStatsdInitCompleted(const int64_t& elapsedTimeNs);
+
// Reset all configs.
void resetConfigs();
@@ -157,6 +181,8 @@ private:
sp<AlarmMonitor> mPeriodicAlarmMonitor;
+ void OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs);
+
void resetIfConfigTtlExpiredLocked(const int64_t timestampNs);
void OnConfigUpdatedLocked(
@@ -170,8 +196,17 @@ private:
void SetConfigsActiveStateLocked(const ActiveConfigList& activeConfigList,
int64_t currentTimeNs);
+ void SetMetadataStateLocked(const metadata::StatsMetadataList& statsMetadataList,
+ int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs);
+
+ void WriteMetadataToProtoLocked(int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs,
+ metadata::StatsMetadataList* metadataList);
+
void WriteDataToDiskLocked(const DumpReportReason dumpReportReason,
const DumpLatency dumpLatency);
+
void WriteDataToDiskLocked(const ConfigKey& key, const int64_t timestampNs,
const DumpReportReason dumpReportReason,
const DumpLatency dumpLatency);
@@ -186,8 +221,7 @@ private:
/* Check if we should send a broadcast if approaching memory limits and if we're over, we
* actually delete the data. */
- void flushIfNecessaryLocked(int64_t timestampNs, const ConfigKey& key,
- MetricsManager& metricsManager);
+ void flushIfNecessaryLocked(const ConfigKey& key, MetricsManager& metricsManager);
// Maps the isolated uid in the log event to host uid if the log event contains uid fields.
void mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const;
@@ -195,6 +229,22 @@ private:
// Handler over the isolated uid change event.
void onIsolatedUidChangedEventLocked(const LogEvent& event);
+ // Handler over the binary push state changed event.
+ void onBinaryPushStateChangedEventLocked(LogEvent* event);
+
+ // Handler over the watchdog rollback occurred event.
+ void onWatchdogRollbackOccurredLocked(LogEvent* event);
+
+ // Updates train info on disk based on binary push state changed info and
+ // write disk info into parameters.
+ void getAndUpdateTrainInfoOnDisk(bool is_rollback, InstallTrainInfo* trainInfoIn);
+
+ // Gets experiment ids on disk for associated train and updates them
+ // depending on rollback type. Then writes them back to disk and returns
+ // them.
+ std::vector<int64_t> processWatchdogRollbackOccurred(const int32_t rollbackTypeIn,
+ const string& packageName);
+
// Reset all configs.
void resetConfigsLocked(const int64_t timestampNs);
// Reset the specified configs.
@@ -223,6 +273,9 @@ private:
// Last time we wrote active metrics to disk.
int64_t mLastActiveMetricsWriteNs = 0;
+ //Last time we wrote metadata to disk.
+ int64_t mLastMetadataWriteNs = 0;
+
#ifdef VERY_VERBOSE_PRINTING
bool mPrintAllLogs = false;
#endif
@@ -231,6 +284,7 @@ private:
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize);
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast);
FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge);
+ FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved);
FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
@@ -254,25 +308,12 @@ private:
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation);
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition);
FRIEND_TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation);
-
- FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_NoLink_OR_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_Link_OR_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_OR_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_OR_CombinationCondition);
-
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_SimpleCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_SimpleCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_SimpleCondition);
-
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_AND_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_AND_CombinationCondition);
FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket);
FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets);
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_written);
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk);
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_load_refractory_from_disk);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period);
@@ -285,12 +326,31 @@ private:
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
+ FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges);
+ FRIEND_TEST(CountMetricE2eTest, TestSlicedState);
+ FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap);
+ FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates);
+ FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields);
+
FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
FRIEND_TEST(DurationMetricE2eTest, TestWithCondition);
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition);
FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedState);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStateMapped);
+ FRIEND_TEST(DurationMetricE2eTest, TestSlicedStatePrimaryFieldsNotSubsetDimInWhat);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset);
+
+ FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation);
+ FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState);
+ FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions);
+ FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions);
};
} // namespace statsd
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index d21c10c6800b..322648229d0e 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -28,14 +28,11 @@
#include <android-base/file.h>
#include <android-base/strings.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/PermissionController.h>
#include <cutils/multiuser.h>
#include <frameworks/base/cmds/statsd/src/statsd_config.pb.h>
#include <frameworks/base/cmds/statsd/src/uid_data.pb.h>
#include <private/android_filesystem_config.h>
-#include <statslog.h>
+#include <statslog_statsd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/system_properties.h>
@@ -48,79 +45,44 @@ using android::base::StringPrintf;
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_MESSAGE;
+using Status = ::ndk::ScopedAStatus;
+
namespace android {
namespace os {
namespace statsd {
constexpr const char* kPermissionDump = "android.permission.DUMP";
-constexpr const char* kPermissionUsage = "android.permission.PACKAGE_USAGE_STATS";
-constexpr const char* kOpUsage = "android:get_usage_stats";
+constexpr const char* kPermissionRegisterPullAtom = "android.permission.REGISTER_STATS_PULL_ATOM";
#define STATS_SERVICE_DIR "/data/misc/stats-service"
// for StatsDataDumpProto
const int FIELD_ID_REPORTS_LIST = 1;
-static binder::Status ok() {
- return binder::Status::ok();
+static Status exception(int32_t code, const std::string& msg) {
+ ALOGE("%s (%d)", msg.c_str(), code);
+ return Status::fromExceptionCodeWithMessage(code, msg.c_str());
}
-static binder::Status exception(uint32_t code, const std::string& msg) {
- ALOGE("%s (%d)", msg.c_str(), code);
- return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
+static bool checkPermission(const char* permission) {
+ pid_t pid = AIBinder_getCallingPid();
+ uid_t uid = AIBinder_getCallingUid();
+ return checkPermissionForIds(permission, pid, uid);
}
-binder::Status checkUid(uid_t expectedUid) {
- uid_t uid = IPCThreadState::self()->getCallingUid();
+Status checkUid(uid_t expectedUid) {
+ uid_t uid = AIBinder_getCallingUid();
if (uid == expectedUid || uid == AID_ROOT) {
- return ok();
+ return Status::ok();
} else {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d is not expected UID %d", uid, expectedUid));
- }
-}
-
-binder::Status checkDumpAndUsageStats(const String16& packageName) {
- pid_t pid = IPCThreadState::self()->getCallingPid();
- uid_t uid = IPCThreadState::self()->getCallingUid();
-
- // Root, system, and shell always have access
- if (uid == AID_ROOT || uid == AID_SYSTEM || uid == AID_SHELL) {
- return ok();
- }
-
- // Caller must be granted these permissions
- if (!checkCallingPermission(String16(kPermissionDump))) {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, kPermissionDump));
- }
- if (!checkCallingPermission(String16(kPermissionUsage))) {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, kPermissionUsage));
- }
-
- // Caller must also have usage stats op granted
- PermissionController pc;
- switch (pc.noteOp(String16(kOpUsage), uid, packageName)) {
- case PermissionController::MODE_ALLOWED:
- case PermissionController::MODE_DEFAULT:
- return ok();
- default:
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d / PID %d lacks app-op %s", uid, pid, kOpUsage));
+ return exception(EX_SECURITY,
+ StringPrintf("UID %d is not expected UID %d", uid, expectedUid));
}
}
#define ENFORCE_UID(uid) { \
- binder::Status status = checkUid((uid)); \
- if (!status.isOk()) { \
- return status; \
- } \
-}
-
-#define ENFORCE_DUMP_AND_USAGE_STATS(packageName) { \
- binder::Status status = checkDumpAndUsageStats(packageName); \
+ Status status = checkUid((uid)); \
if (!status.isOk()) { \
return status; \
} \
@@ -129,13 +91,13 @@ binder::Status checkDumpAndUsageStats(const String16& packageName) {
StatsService::StatsService(const sp<Looper>& handlerLooper, shared_ptr<LogEventQueue> queue)
: mAnomalyAlarmMonitor(new AlarmMonitor(
MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS,
- [](const sp<IStatsCompanionService>& sc, int64_t timeMillis) {
+ [](const shared_ptr<IStatsCompanionService>& sc, int64_t timeMillis) {
if (sc != nullptr) {
sc->setAnomalyAlarm(timeMillis);
StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
}
},
- [](const sp<IStatsCompanionService>& sc) {
+ [](const shared_ptr<IStatsCompanionService>& sc) {
if (sc != nullptr) {
sc->cancelAnomalyAlarm();
StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
@@ -143,19 +105,23 @@ StatsService::StatsService(const sp<Looper>& handlerLooper, shared_ptr<LogEventQ
})),
mPeriodicAlarmMonitor(new AlarmMonitor(
MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS,
- [](const sp<IStatsCompanionService>& sc, int64_t timeMillis) {
+ [](const shared_ptr<IStatsCompanionService>& sc, int64_t timeMillis) {
if (sc != nullptr) {
sc->setAlarmForSubscriberTriggering(timeMillis);
StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged();
}
},
- [](const sp<IStatsCompanionService>& sc) {
+ [](const shared_ptr<IStatsCompanionService>& sc) {
if (sc != nullptr) {
sc->cancelAlarmForSubscriberTriggering();
StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged();
}
})),
- mEventQueue(queue) {
+ mEventQueue(queue),
+ mBootCompleteTrigger({kBootCompleteTag, kUidMapReceivedTag, kAllPullersRegisteredTag},
+ [this]() { mProcessor->onStatsdInitCompleted(getElapsedRealtimeNs()); }),
+ mStatsCompanionServiceDeathRecipient(
+ AIBinder_DeathRecipient_new(StatsService::statsCompanionServiceDied)) {
mUidMap = UidMap::getInstance();
mPullerManager = new StatsPullerManager();
StatsPuller::SetUidMap(mUidMap);
@@ -164,33 +130,30 @@ StatsService::StatsService(const sp<Looper>& handlerLooper, shared_ptr<LogEventQ
mUidMap, mPullerManager, mAnomalyAlarmMonitor, mPeriodicAlarmMonitor,
getElapsedRealtimeNs(),
[this](const ConfigKey& key) {
- sp<IStatsCompanionService> sc = getStatsCompanionService();
- auto receiver = mConfigManager->GetConfigReceiver(key);
- if (sc == nullptr) {
- VLOG("Could not find StatsCompanionService");
+ shared_ptr<IPendingIntentRef> receiver = mConfigManager->GetConfigReceiver(key);
+ if (receiver == nullptr) {
+ VLOG("Could not find a broadcast receiver for %s", key.ToString().c_str());
return false;
- } else if (receiver == nullptr) {
- VLOG("Statscompanion could not find a broadcast receiver for %s",
- key.ToString().c_str());
- return false;
- } else {
- sc->sendDataBroadcast(receiver, mProcessor->getLastReportTimeNs(key));
+ } else if (receiver->sendDataBroadcast(
+ mProcessor->getLastReportTimeNs(key)).isOk()) {
return true;
+ } else {
+ VLOG("Failed to send a broadcast for receiver %s", key.ToString().c_str());
+ return false;
}
},
[this](const int& uid, const vector<int64_t>& activeConfigs) {
- auto receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid);
- sp<IStatsCompanionService> sc = getStatsCompanionService();
- if (sc == nullptr) {
- VLOG("Could not access statsCompanion");
- return false;
- } else if (receiver == nullptr) {
+ shared_ptr<IPendingIntentRef> receiver =
+ mConfigManager->GetActiveConfigsChangedReceiver(uid);
+ if (receiver == nullptr) {
VLOG("Could not find receiver for uid %d", uid);
return false;
- } else {
- sc->sendActiveConfigsChangedBroadcast(receiver, activeConfigs);
+ } else if (receiver->sendActiveConfigsChangedBroadcast(activeConfigs).isOk()) {
VLOG("StatsService::active configs broadcast succeeded for uid %d" , uid);
return true;
+ } else {
+ VLOG("StatsService::active configs broadcast failed for uid %d" , uid);
+ return false;
}
});
@@ -241,36 +204,6 @@ void StatsService::init_build_type_callback(void* cookie, const char* /*name*/,
}
/**
- * Implement our own because the default binder implementation isn't
- * properly handling SHELL_COMMAND_TRANSACTION.
- */
-status_t StatsService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags) {
- switch (code) {
- case SHELL_COMMAND_TRANSACTION: {
- int in = data.readFileDescriptor();
- int out = data.readFileDescriptor();
- int err = data.readFileDescriptor();
- int argc = data.readInt32();
- Vector<String8> args;
- for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
- args.add(String8(data.readString16()));
- }
- sp<IShellCallback> shellCallback = IShellCallback::asInterface(data.readStrongBinder());
- sp<IResultReceiver> resultReceiver =
- IResultReceiver::asInterface(data.readStrongBinder());
-
- err = command(in, out, err, args, resultReceiver);
- if (resultReceiver != nullptr) {
- resultReceiver->send(err);
- }
- return NO_ERROR;
- }
- default: { return BnStatsManager::onTransact(code, data, reply, flags); }
- }
-}
-
-/**
* Write data from statsd.
* Format for statsdStats: adb shell dumpsys stats --metadata [-v] [--proto]
* Format for data report: adb shell dumpsys stats [anything other than --metadata] [--proto]
@@ -279,20 +212,21 @@ status_t StatsService::onTransact(uint32_t code, const Parcel& data, Parcel* rep
* (bugreports call "adb shell dumpsys stats --dump-priority NORMAL -a --proto")
* TODO: Come up with a more robust method of enacting <serviceutils/PriorityDumper.h>.
*/
-status_t StatsService::dump(int fd, const Vector<String16>& args) {
- if (!checkCallingPermission(String16(kPermissionDump))) {
+status_t StatsService::dump(int fd, const char** args, uint32_t numArgs) {
+ if (!checkPermission(kPermissionDump)) {
return PERMISSION_DENIED;
}
- int lastArg = args.size() - 1;
+
+ int lastArg = numArgs - 1;
bool asProto = false;
- if (lastArg >= 0 && !args[lastArg].compare(String16("--proto"))) { // last argument
+ if (lastArg >= 0 && string(args[lastArg]) == "--proto") { // last argument
asProto = true;
lastArg--;
}
- if (args.size() > 0 && !args[0].compare(String16("--metadata"))) { // first argument
+ if (numArgs > 0 && string(args[0]) == "--metadata") { // first argument
// Request is to dump statsd stats.
bool verbose = false;
- if (lastArg >= 0 && !args[lastArg].compare(String16("-v"))) {
+ if (lastArg >= 0 && string(args[lastArg]) == "-v") {
verbose = true;
lastArg--;
}
@@ -333,8 +267,11 @@ void StatsService::dumpIncidentSection(int out) {
for (const ConfigKey& configKey : mConfigManager->GetAllConfigKeys()) {
uint64_t reportsListToken =
proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS_LIST);
+ // Don't include the current bucket to avoid skipping buckets.
+ // If we need to include the current bucket later, consider changing to NO_TIME_CONSTRAINTS
+ // or other alternatives to avoid skipping buckets for pulled metrics.
mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(),
- true /* includeCurrentBucket */, false /* erase_data */,
+ false /* includeCurrentBucket */, false /* erase_data */,
ADB_DUMP,
FAST,
&proto);
@@ -347,67 +284,74 @@ void StatsService::dumpIncidentSection(int out) {
/**
* Implementation of the adb shell cmd stats command.
*/
-status_t StatsService::command(int in, int out, int err, Vector<String8>& args,
- sp<IResultReceiver> resultReceiver) {
- uid_t uid = IPCThreadState::self()->getCallingUid();
+status_t StatsService::handleShellCommand(int in, int out, int err, const char** argv,
+ uint32_t argc) {
+ uid_t uid = AIBinder_getCallingUid();
if (uid != AID_ROOT && uid != AID_SHELL) {
return PERMISSION_DENIED;
}
- const int argCount = args.size();
- if (argCount >= 1) {
+ Vector<String8> utf8Args;
+ utf8Args.setCapacity(argc);
+ for (uint32_t i = 0; i < argc; i++) {
+ utf8Args.push(String8(argv[i]));
+ }
+
+ if (argc >= 1) {
// adb shell cmd stats config ...
- if (!args[0].compare(String8("config"))) {
- return cmd_config(in, out, err, args);
+ if (!utf8Args[0].compare(String8("config"))) {
+ return cmd_config(in, out, err, utf8Args);
}
- if (!args[0].compare(String8("print-uid-map"))) {
- return cmd_print_uid_map(out, args);
+ if (!utf8Args[0].compare(String8("print-uid-map"))) {
+ return cmd_print_uid_map(out, utf8Args);
}
- if (!args[0].compare(String8("dump-report"))) {
- return cmd_dump_report(out, args);
+ if (!utf8Args[0].compare(String8("dump-report"))) {
+ return cmd_dump_report(out, utf8Args);
}
- if (!args[0].compare(String8("pull-source")) && args.size() > 1) {
- return cmd_print_pulled_metrics(out, args);
+ if (!utf8Args[0].compare(String8("pull-source")) && argc > 1) {
+ return cmd_print_pulled_metrics(out, utf8Args);
}
- if (!args[0].compare(String8("send-broadcast"))) {
- return cmd_trigger_broadcast(out, args);
+ if (!utf8Args[0].compare(String8("send-broadcast"))) {
+ return cmd_trigger_broadcast(out, utf8Args);
}
- if (!args[0].compare(String8("print-stats"))) {
- return cmd_print_stats(out, args);
+ if (!utf8Args[0].compare(String8("print-stats"))) {
+ return cmd_print_stats(out, utf8Args);
}
- if (!args[0].compare(String8("meminfo"))) {
+ if (!utf8Args[0].compare(String8("meminfo"))) {
return cmd_dump_memory_info(out);
}
- if (!args[0].compare(String8("write-to-disk"))) {
+ if (!utf8Args[0].compare(String8("write-to-disk"))) {
return cmd_write_data_to_disk(out);
}
- if (!args[0].compare(String8("log-app-breadcrumb"))) {
- return cmd_log_app_breadcrumb(out, args);
+ if (!utf8Args[0].compare(String8("log-app-breadcrumb"))) {
+ return cmd_log_app_breadcrumb(out, utf8Args);
}
- if (!args[0].compare(String8("log-binary-push"))) {
- return cmd_log_binary_push(out, args);
+ if (!utf8Args[0].compare(String8("log-binary-push"))) {
+ return cmd_log_binary_push(out, utf8Args);
}
- if (!args[0].compare(String8("clear-puller-cache"))) {
+ if (!utf8Args[0].compare(String8("clear-puller-cache"))) {
return cmd_clear_puller_cache(out);
}
- if (!args[0].compare(String8("print-logs"))) {
- return cmd_print_logs(out, args);
+ if (!utf8Args[0].compare(String8("print-logs"))) {
+ return cmd_print_logs(out, utf8Args);
}
- if (!args[0].compare(String8("send-active-configs"))) {
- return cmd_trigger_active_config_broadcast(out, args);
+
+ if (!utf8Args[0].compare(String8("send-active-configs"))) {
+ return cmd_trigger_active_config_broadcast(out, utf8Args);
}
- if (!args[0].compare(String8("data-subscribe"))) {
+
+ if (!utf8Args[0].compare(String8("data-subscribe"))) {
{
std::lock_guard<std::mutex> lock(mShellSubscriberMutex);
if (mShellSubscriber == nullptr) {
@@ -415,14 +359,10 @@ status_t StatsService::command(int in, int out, int err, Vector<String8>& args,
}
}
int timeoutSec = -1;
- if (argCount >= 2) {
- timeoutSec = atoi(args[1].c_str());
+ if (argc >= 2) {
+ timeoutSec = atoi(utf8Args[1].c_str());
}
- if (resultReceiver == nullptr) {
- ALOGI("Null resultReceiver given, no subscription will be started");
- return UNEXPECTED_NULL;
- }
- mShellSubscriber->startNewSubscription(in, out, resultReceiver, timeoutSec);
+ mShellSubscriber->startNewSubscription(in, out, timeoutSec);
return NO_ERROR;
}
}
@@ -452,9 +392,11 @@ void StatsService::print_cmd_help(int out) {
dprintf(out, " PKG Optional package name to print the uids of the package\n");
dprintf(out, "\n");
dprintf(out, "\n");
- dprintf(out, "usage: adb shell cmd stats pull-source [int] \n");
+ dprintf(out, "usage: adb shell cmd stats pull-source ATOM_TAG [PACKAGE] \n");
dprintf(out, "\n");
- dprintf(out, " Prints the output of a pulled metrics source (int indicates source)\n");
+ dprintf(out, " Prints the output of a pulled atom\n");
+ dprintf(out, " UID The atom to pull\n");
+ dprintf(out, " PACKAGE The package to pull from. Default is AID_SYSTEM\n");
dprintf(out, "\n");
dprintf(out, "\n");
dprintf(out, "usage: adb shell cmd stats write-to-disk \n");
@@ -552,7 +494,7 @@ status_t StatsService::cmd_trigger_broadcast(int out, Vector<String8>& args) {
const int argCount = args.size();
if (argCount == 2) {
// Automatically pick the UID
- uid = IPCThreadState::self()->getCallingUid();
+ uid = AIBinder_getCallingUid();
name.assign(args[1].c_str(), args[1].size());
good = true;
} else if (argCount == 3) {
@@ -568,18 +510,17 @@ status_t StatsService::cmd_trigger_broadcast(int out, Vector<String8>& args) {
return UNKNOWN_ERROR;
}
ConfigKey key(uid, StrToInt64(name));
- auto receiver = mConfigManager->GetConfigReceiver(key);
- sp<IStatsCompanionService> sc = getStatsCompanionService();
- if (sc == nullptr) {
- VLOG("Could not access statsCompanion");
- } else if (receiver == nullptr) {
- VLOG("Could not find receiver for %s, %s", args[1].c_str(), args[2].c_str())
- } else {
- sc->sendDataBroadcast(receiver, mProcessor->getLastReportTimeNs(key));
+ shared_ptr<IPendingIntentRef> receiver = mConfigManager->GetConfigReceiver(key);
+ if (receiver == nullptr) {
+ VLOG("Could not find receiver for %s, %s", args[1].c_str(), args[2].c_str());
+ return UNKNOWN_ERROR;
+ } else if (receiver->sendDataBroadcast(mProcessor->getLastReportTimeNs(key)).isOk()) {
VLOG("StatsService::trigger broadcast succeeded to %s, %s", args[1].c_str(),
args[2].c_str());
+ } else {
+ VLOG("StatsService::trigger broadcast failed to %s, %s", args[1].c_str(), args[2].c_str());
+ return UNKNOWN_ERROR;
}
-
return NO_ERROR;
}
@@ -589,7 +530,7 @@ status_t StatsService::cmd_trigger_active_config_broadcast(int out, Vector<Strin
vector<int64_t> configIds;
if (argCount == 1) {
// Automatically pick the uid and send a broadcast that has no active configs.
- uid = IPCThreadState::self()->getCallingUid();
+ uid = AIBinder_getCallingUid();
mProcessor->GetActiveConfigs(uid, configIds);
} else {
int curArg = 1;
@@ -603,7 +544,7 @@ status_t StatsService::cmd_trigger_active_config_broadcast(int out, Vector<Strin
}
curArg++;
} else {
- uid = IPCThreadState::self()->getCallingUid();
+ uid = AIBinder_getCallingUid();
}
if (curArg == argCount || args[curArg] != "--configs") {
VLOG("Reached end of args, or specify configs not set. Sending actual active configs,");
@@ -623,15 +564,15 @@ status_t StatsService::cmd_trigger_active_config_broadcast(int out, Vector<Strin
}
}
}
- auto receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid);
- sp<IStatsCompanionService> sc = getStatsCompanionService();
- if (sc == nullptr) {
- VLOG("Could not access statsCompanion");
- } else if (receiver == nullptr) {
+ shared_ptr<IPendingIntentRef> receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid);
+ if (receiver == nullptr) {
VLOG("Could not find receiver for uid %d", uid);
- } else {
- sc->sendActiveConfigsChangedBroadcast(receiver, configIds);
+ return UNKNOWN_ERROR;
+ } else if (receiver->sendActiveConfigsChangedBroadcast(configIds).isOk()) {
VLOG("StatsService::trigger active configs changed broadcast succeeded for uid %d" , uid);
+ } else {
+ VLOG("StatsService::trigger active configs changed broadcast failed for uid %d", uid);
+ return UNKNOWN_ERROR;
}
return NO_ERROR;
}
@@ -646,7 +587,7 @@ status_t StatsService::cmd_config(int in, int out, int err, Vector<String8>& arg
if (argCount == 3) {
// Automatically pick the UID
- uid = IPCThreadState::self()->getCallingUid();
+ uid = AIBinder_getCallingUid();
name.assign(args[2].c_str(), args[2].size());
good = true;
} else if (argCount == 4) {
@@ -729,7 +670,7 @@ status_t StatsService::cmd_dump_report(int out, const Vector<String8>& args) {
}
if (argCount == 2) {
// Automatically pick the UID
- uid = IPCThreadState::self()->getCallingUid();
+ uid = AIBinder_getCallingUid();
name.assign(args[1].c_str(), args[1].size());
good = true;
} else if (argCount == 3) {
@@ -821,7 +762,7 @@ status_t StatsService::cmd_log_app_breadcrumb(int out, const Vector<String8>& ar
const int argCount = args.size();
if (argCount == 3) {
// Automatically pick the UID
- uid = IPCThreadState::self()->getCallingUid();
+ uid = AIBinder_getCallingUid();
label = atoi(args[1].c_str());
state = atoi(args[2].c_str());
good = true;
@@ -837,7 +778,8 @@ status_t StatsService::cmd_log_app_breadcrumb(int out, const Vector<String8>& ar
}
if (good) {
dprintf(out, "Logging AppBreadcrumbReported(%d, %d, %d) to statslog.\n", uid, label, state);
- android::util::stats_write(android::util::APP_BREADCRUMB_REPORTED, uid, label, state);
+ android::os::statsd::util::stats_write(
+ android::os::statsd::util::APP_BREADCRUMB_REPORTED, uid, label, state);
} else {
print_cmd_help(out);
return UNKNOWN_ERROR;
@@ -852,18 +794,8 @@ status_t StatsService::cmd_log_binary_push(int out, const Vector<String8>& args)
dprintf(out, "Incorrect number of argument supplied\n");
return UNKNOWN_ERROR;
}
- android::String16 trainName = android::String16(args[1].c_str());
+ string trainName = string(args[1].c_str());
int64_t trainVersion = strtoll(args[2].c_str(), nullptr, 10);
- int options = 0;
- if (args[3] == "1") {
- options = options | IStatsManager::FLAG_REQUIRE_STAGING;
- }
- if (args[4] == "1") {
- options = options | IStatsManager::FLAG_ROLLBACK_ENABLED;
- }
- if (args[5] == "1") {
- options = options | IStatsManager::FLAG_REQUIRE_LOW_LATENCY_MONITOR;
- }
int32_t state = atoi(args[6].c_str());
vector<int64_t> experimentIds;
if (argCount == 8) {
@@ -874,14 +806,30 @@ status_t StatsService::cmd_log_binary_push(int out, const Vector<String8>& args)
}
}
dprintf(out, "Logging BinaryPushStateChanged\n");
- sendBinaryPushStateChangedAtom(trainName, trainVersion, options, state, experimentIds);
+ vector<uint8_t> experimentIdBytes;
+ writeExperimentIdsToProto(experimentIds, &experimentIdBytes);
+ LogEvent event(trainName, trainVersion, args[3], args[4], args[5], state, experimentIdBytes, 0);
+ mProcessor->OnLogEvent(&event);
return NO_ERROR;
}
status_t StatsService::cmd_print_pulled_metrics(int out, const Vector<String8>& args) {
int s = atoi(args[1].c_str());
- vector<shared_ptr<LogEvent> > stats;
- if (mPullerManager->Pull(s, &stats)) {
+ vector<int32_t> uids;
+ if (args.size() > 2) {
+ string package = string(args[2].c_str());
+ auto it = UidMap::sAidToUidMapping.find(package);
+ if (it != UidMap::sAidToUidMapping.end()) {
+ uids.push_back(it->second);
+ } else {
+ set<int32_t> uids_set = mUidMap->getAppUid(package);
+ uids.insert(uids.end(), uids_set.begin(), uids_set.end());
+ }
+ } else {
+ uids.push_back(AID_SYSTEM);
+ }
+ vector<shared_ptr<LogEvent>> stats;
+ if (mPullerManager->Pull(s, uids, getElapsedRealtimeNs(), &stats)) {
for (const auto& it : stats) {
dprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str());
}
@@ -905,10 +853,9 @@ status_t StatsService::cmd_dump_memory_info(int out) {
}
status_t StatsService::cmd_clear_puller_cache(int out) {
- IPCThreadState* ipc = IPCThreadState::self();
VLOG("StatsService::cmd_clear_puller_cache with Pid %i, Uid %i",
- ipc->getCallingPid(), ipc->getCallingUid());
- if (checkCallingPermission(String16(kPermissionDump))) {
+ AIBinder_getCallingPid(), AIBinder_getCallingUid());
+ if (checkPermission(kPermissionDump)) {
int cleared = mPullerManager->ForceClearPullerCache();
dprintf(out, "Puller removed %d cached data!\n", cleared);
return NO_ERROR;
@@ -918,10 +865,9 @@ status_t StatsService::cmd_clear_puller_cache(int out) {
}
status_t StatsService::cmd_print_logs(int out, const Vector<String8>& args) {
- IPCThreadState* ipc = IPCThreadState::self();
- VLOG("StatsService::cmd_print_logs with Pid %i, Uid %i", ipc->getCallingPid(),
- ipc->getCallingUid());
- if (checkCallingPermission(String16(kPermissionDump))) {
+ VLOG("StatsService::cmd_print_logs with Pid %i, Uid %i", AIBinder_getCallingPid(),
+ AIBinder_getCallingUid());
+ if (checkPermission(kPermissionDump)) {
bool enabled = true;
if (args.size() >= 2) {
enabled = atoi(args[1].c_str()) != 0;
@@ -952,24 +898,24 @@ bool StatsService::getUidFromString(const char* s, int32_t& uid) {
}
uid = goodUid;
- int32_t callingUid = IPCThreadState::self()->getCallingUid();
+ int32_t callingUid = AIBinder_getCallingUid();
return mEngBuild // UserDebug/EngBuild are allowed to impersonate uids.
|| (callingUid == goodUid) // Anyone can 'impersonate' themselves.
|| (callingUid == AID_ROOT && goodUid == AID_SHELL); // ROOT can impersonate SHELL.
}
-Status StatsService::informAllUidData(const ParcelFileDescriptor& fd) {
+Status StatsService::informAllUidData(const ScopedFileDescriptor& fd) {
ENFORCE_UID(AID_SYSTEM);
// Read stream into buffer.
string buffer;
if (!android::base::ReadFdToString(fd.get(), &buffer)) {
- return exception(Status::EX_ILLEGAL_ARGUMENT, "Failed to read all data from the pipe.");
+ return exception(EX_ILLEGAL_ARGUMENT, "Failed to read all data from the pipe.");
}
// Parse buffer.
UidData uidData;
if (!uidData.ParseFromString(buffer)) {
- return exception(Status::EX_ILLEGAL_ARGUMENT, "Error parsing proto stream for UidData.");
+ return exception(EX_ILLEGAL_ARGUMENT, "Error parsing proto stream for UidData.");
}
vector<String16> versionStrings;
@@ -1000,24 +946,31 @@ Status StatsService::informAllUidData(const ParcelFileDescriptor& fd) {
packageNames,
installers);
+ mBootCompleteTrigger.markComplete(kUidMapReceivedTag);
VLOG("StatsService::informAllUidData UidData proto parsed successfully.");
return Status::ok();
}
-Status StatsService::informOnePackage(const String16& app, int32_t uid, int64_t version,
- const String16& version_string, const String16& installer) {
+Status StatsService::informOnePackage(const string& app, int32_t uid, int64_t version,
+ const string& versionString, const string& installer) {
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::informOnePackage was called");
- mUidMap->updateApp(getElapsedRealtimeNs(), app, uid, version, version_string, installer);
+ String16 utf16App = String16(app.c_str());
+ String16 utf16VersionString = String16(versionString.c_str());
+ String16 utf16Installer = String16(installer.c_str());
+
+ mUidMap->updateApp(getElapsedRealtimeNs(), utf16App, uid, version, utf16VersionString,
+ utf16Installer);
return Status::ok();
}
-Status StatsService::informOnePackageRemoved(const String16& app, int32_t uid) {
+Status StatsService::informOnePackageRemoved(const string& app, int32_t uid) {
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::informOnePackageRemoved was called");
- mUidMap->removeApp(getElapsedRealtimeNs(), app, uid);
+ String16 utf16App = String16(app.c_str());
+ mUidMap->removeApp(getElapsedRealtimeNs(), utf16App, uid);
mConfigManager->RemoveConfigs(uid);
return Status::ok();
}
@@ -1077,11 +1030,12 @@ Status StatsService::informDeviceShutdown() {
VLOG("StatsService::informDeviceShutdown");
mProcessor->WriteDataToDisk(DEVICE_SHUTDOWN, FAST);
mProcessor->SaveActiveConfigsToDisk(getElapsedRealtimeNs());
+ mProcessor->SaveMetadataToDisk(getWallClockNs(), getElapsedRealtimeNs());
return Status::ok();
}
void StatsService::sayHiToStatsCompanion() {
- sp<IStatsCompanionService> statsCompanion = getStatsCompanionService();
+ shared_ptr<IStatsCompanionService> statsCompanion = getStatsCompanionService();
if (statsCompanion != nullptr) {
VLOG("Telling statsCompanion that statsd is ready");
statsCompanion->statsdReady();
@@ -1094,24 +1048,32 @@ Status StatsService::statsCompanionReady() {
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::statsCompanionReady was called");
- sp<IStatsCompanionService> statsCompanion = getStatsCompanionService();
+ shared_ptr<IStatsCompanionService> statsCompanion = getStatsCompanionService();
if (statsCompanion == nullptr) {
- return Status::fromExceptionCode(
- Status::EX_NULL_POINTER,
- "statscompanion unavailable despite it contacting statsd!");
+ return exception(EX_NULL_POINTER,
+ "StatsCompanion unavailable despite it contacting statsd.");
}
VLOG("StatsService::statsCompanionReady linking to statsCompanion.");
- IInterface::asBinder(statsCompanion)->linkToDeath(this);
+ AIBinder_linkToDeath(statsCompanion->asBinder().get(),
+ mStatsCompanionServiceDeathRecipient.get(), this);
mPullerManager->SetStatsCompanionService(statsCompanion);
mAnomalyAlarmMonitor->setStatsCompanionService(statsCompanion);
mPeriodicAlarmMonitor->setStatsCompanionService(statsCompanion);
- SubscriberReporter::getInstance().setStatsCompanionService(statsCompanion);
+ return Status::ok();
+}
+
+Status StatsService::bootCompleted() {
+ ENFORCE_UID(AID_SYSTEM);
+
+ VLOG("StatsService::bootCompleted was called");
+ mBootCompleteTrigger.markComplete(kBootCompleteTag);
return Status::ok();
}
void StatsService::Startup() {
mConfigManager->Startup();
mProcessor->LoadActiveConfigsFromDisk();
+ mProcessor->LoadMetadataFromDisk(getWallClockNs(), getElapsedRealtimeNs());
}
void StatsService::Terminate() {
@@ -1119,6 +1081,7 @@ void StatsService::Terminate() {
if (mProcessor != nullptr) {
mProcessor->WriteDataToDisk(TERMINATION_SIGNAL_RECEIVED, FAST);
mProcessor->SaveActiveConfigsToDisk(getElapsedRealtimeNs());
+ mProcessor->SaveMetadataToDisk(getWallClockNs(), getElapsedRealtimeNs());
}
}
@@ -1130,12 +1093,11 @@ void StatsService::OnLogEvent(LogEvent* event) {
}
}
-Status StatsService::getData(int64_t key, const String16& packageName, vector<uint8_t>* output) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+Status StatsService::getData(int64_t key, const int32_t callingUid, vector<uint8_t>* output) {
+ ENFORCE_UID(AID_SYSTEM);
- IPCThreadState* ipc = IPCThreadState::self();
- VLOG("StatsService::getData with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid());
- ConfigKey configKey(ipc->getCallingUid(), key);
+ VLOG("StatsService::getData with Uid %i", callingUid);
+ ConfigKey configKey(callingUid, key);
// The dump latency does not matter here since we do not include the current bucket, we do not
// need to pull any new data anyhow.
mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(), false /* include_current_bucket*/,
@@ -1143,27 +1105,21 @@ Status StatsService::getData(int64_t key, const String16& packageName, vector<ui
return Status::ok();
}
-Status StatsService::getMetadata(const String16& packageName, vector<uint8_t>* output) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+Status StatsService::getMetadata(vector<uint8_t>* output) {
+ ENFORCE_UID(AID_SYSTEM);
- IPCThreadState* ipc = IPCThreadState::self();
- VLOG("StatsService::getMetadata with Pid %i, Uid %i", ipc->getCallingPid(),
- ipc->getCallingUid());
StatsdStats::getInstance().dumpStats(output, false); // Don't reset the counters.
return Status::ok();
}
Status StatsService::addConfiguration(int64_t key, const vector <uint8_t>& config,
- const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+ const int32_t callingUid) {
+ ENFORCE_UID(AID_SYSTEM);
- IPCThreadState* ipc = IPCThreadState::self();
- if (addConfigurationChecked(ipc->getCallingUid(), key, config)) {
+ if (addConfigurationChecked(callingUid, key, config)) {
return Status::ok();
} else {
- ALOGE("Could not parse malformatted StatsdConfig");
- return Status::fromExceptionCode(binder::Status::EX_ILLEGAL_ARGUMENT,
- "config does not correspond to a StatsdConfig proto");
+ return exception(EX_ILLEGAL_ARGUMENT, "Could not parse malformatted StatsdConfig.");
}
}
@@ -1179,23 +1135,21 @@ bool StatsService::addConfigurationChecked(int uid, int64_t key, const vector<ui
return true;
}
-Status StatsService::removeDataFetchOperation(int64_t key, const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
-
- IPCThreadState* ipc = IPCThreadState::self();
- ConfigKey configKey(ipc->getCallingUid(), key);
+Status StatsService::removeDataFetchOperation(int64_t key,
+ const int32_t callingUid) {
+ ENFORCE_UID(AID_SYSTEM);
+ ConfigKey configKey(callingUid, key);
mConfigManager->RemoveConfigReceiver(configKey);
return Status::ok();
}
Status StatsService::setDataFetchOperation(int64_t key,
- const sp<android::IBinder>& intentSender,
- const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+ const shared_ptr<IPendingIntentRef>& pir,
+ const int32_t callingUid) {
+ ENFORCE_UID(AID_SYSTEM);
- IPCThreadState* ipc = IPCThreadState::self();
- ConfigKey configKey(ipc->getCallingUid(), key);
- mConfigManager->SetConfigReceiver(configKey, intentSender);
+ ConfigKey configKey(callingUid, key);
+ mConfigManager->SetConfigReceiver(configKey, pir);
if (StorageManager::hasConfigMetricsReport(configKey)) {
VLOG("StatsService::setDataFetchOperation marking configKey %s to dump reports on disk",
configKey.ToString().c_str());
@@ -1204,301 +1158,170 @@ Status StatsService::setDataFetchOperation(int64_t key,
return Status::ok();
}
-Status StatsService::setActiveConfigsChangedOperation(const sp<android::IBinder>& intentSender,
- const String16& packageName,
+Status StatsService::setActiveConfigsChangedOperation(const shared_ptr<IPendingIntentRef>& pir,
+ const int32_t callingUid,
vector<int64_t>* output) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+ ENFORCE_UID(AID_SYSTEM);
- IPCThreadState* ipc = IPCThreadState::self();
- int uid = ipc->getCallingUid();
- mConfigManager->SetActiveConfigsChangedReceiver(uid, intentSender);
+ mConfigManager->SetActiveConfigsChangedReceiver(callingUid, pir);
if (output != nullptr) {
- mProcessor->GetActiveConfigs(uid, *output);
+ mProcessor->GetActiveConfigs(callingUid, *output);
} else {
ALOGW("StatsService::setActiveConfigsChanged output was nullptr");
}
return Status::ok();
}
-Status StatsService::removeActiveConfigsChangedOperation(const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+Status StatsService::removeActiveConfigsChangedOperation(const int32_t callingUid) {
+ ENFORCE_UID(AID_SYSTEM);
- IPCThreadState* ipc = IPCThreadState::self();
- mConfigManager->RemoveActiveConfigsChangedReceiver(ipc->getCallingUid());
+ mConfigManager->RemoveActiveConfigsChangedReceiver(callingUid);
return Status::ok();
}
-Status StatsService::removeConfiguration(int64_t key, const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+Status StatsService::removeConfiguration(int64_t key, const int32_t callingUid) {
+ ENFORCE_UID(AID_SYSTEM);
- IPCThreadState* ipc = IPCThreadState::self();
- ConfigKey configKey(ipc->getCallingUid(), key);
+ ConfigKey configKey(callingUid, key);
mConfigManager->RemoveConfig(configKey);
- SubscriberReporter::getInstance().removeConfig(configKey);
return Status::ok();
}
Status StatsService::setBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
- const sp<android::IBinder>& intentSender,
- const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+ const shared_ptr<IPendingIntentRef>& pir,
+ const int32_t callingUid) {
+ ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::setBroadcastSubscriber called.");
- IPCThreadState* ipc = IPCThreadState::self();
- ConfigKey configKey(ipc->getCallingUid(), configId);
+ ConfigKey configKey(callingUid, configId);
SubscriberReporter::getInstance()
- .setBroadcastSubscriber(configKey, subscriberId, intentSender);
+ .setBroadcastSubscriber(configKey, subscriberId, pir);
return Status::ok();
}
Status StatsService::unsetBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
- const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+ const int32_t callingUid) {
+ ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::unsetBroadcastSubscriber called.");
- IPCThreadState* ipc = IPCThreadState::self();
- ConfigKey configKey(ipc->getCallingUid(), configId);
+ ConfigKey configKey(callingUid, configId);
SubscriberReporter::getInstance()
.unsetBroadcastSubscriber(configKey, subscriberId);
return Status::ok();
}
-Status StatsService::sendAppBreadcrumbAtom(int32_t label, int32_t state) {
- // Permission check not necessary as it's meant for applications to write to
- // statsd.
- android::util::stats_write(util::APP_BREADCRUMB_REPORTED,
- IPCThreadState::self()->getCallingUid(), label,
- state);
+Status StatsService::allPullersFromBootRegistered() {
+ ENFORCE_UID(AID_SYSTEM);
+
+ VLOG("StatsService::allPullersFromBootRegistered was called");
+ mBootCompleteTrigger.markComplete(kAllPullersRegisteredTag);
return Status::ok();
}
-Status StatsService::registerPullerCallback(int32_t atomTag,
- const sp<android::os::IStatsPullerCallback>& pullerCallback,
- const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
-
- VLOG("StatsService::registerPullerCallback called.");
- mPullerManager->RegisterPullerCallback(atomTag, pullerCallback);
+Status StatsService::registerPullAtomCallback(int32_t uid, int32_t atomTag, int64_t coolDownMillis,
+ int64_t timeoutMillis,
+ const std::vector<int32_t>& additiveFields,
+ const shared_ptr<IPullAtomCallback>& pullerCallback) {
+ ENFORCE_UID(AID_SYSTEM);
+ VLOG("StatsService::registerPullAtomCallback called.");
+ mPullerManager->RegisterPullAtomCallback(uid, atomTag, MillisToNano(coolDownMillis),
+ MillisToNano(timeoutMillis), additiveFields,
+ pullerCallback);
return Status::ok();
}
-Status StatsService::unregisterPullerCallback(int32_t atomTag, const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
-
- VLOG("StatsService::unregisterPullerCallback called.");
- mPullerManager->UnregisterPullerCallback(atomTag);
+Status StatsService::registerNativePullAtomCallback(
+ int32_t atomTag, int64_t coolDownMillis, int64_t timeoutMillis,
+ const std::vector<int32_t>& additiveFields,
+ const shared_ptr<IPullAtomCallback>& pullerCallback) {
+ if (!checkPermission(kPermissionRegisterPullAtom)) {
+ return exception(
+ EX_SECURITY,
+ StringPrintf("Uid %d does not have the %s permission when registering atom %d",
+ AIBinder_getCallingUid(), kPermissionRegisterPullAtom, atomTag));
+ }
+ VLOG("StatsService::registerNativePullAtomCallback called.");
+ int32_t uid = AIBinder_getCallingUid();
+ mPullerManager->RegisterPullAtomCallback(uid, atomTag, MillisToNano(coolDownMillis),
+ MillisToNano(timeoutMillis), additiveFields,
+ pullerCallback);
return Status::ok();
}
-Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& trainNameIn,
- const int64_t trainVersionCodeIn,
- const int options,
- const int32_t state,
- const std::vector<int64_t>& experimentIdsIn) {
- // Note: We skip the usage stats op check here since we do not have a package name.
- // This is ok since we are overloading the usage_stats permission.
- // This method only sends data, it does not receive it.
- pid_t pid = IPCThreadState::self()->getCallingPid();
- uid_t uid = IPCThreadState::self()->getCallingUid();
- // Root, system, and shell always have access
- if (uid != AID_ROOT && uid != AID_SYSTEM && uid != AID_SHELL) {
- // Caller must be granted these permissions
- if (!checkCallingPermission(String16(kPermissionDump))) {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
- kPermissionDump));
- }
- if (!checkCallingPermission(String16(kPermissionUsage))) {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
- kPermissionUsage));
- }
- }
-
- bool readTrainInfoSuccess = false;
- InstallTrainInfo trainInfoOnDisk;
- readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfoOnDisk);
-
- bool resetExperimentIds = false;
- int64_t trainVersionCode = trainVersionCodeIn;
- std::string trainNameUtf8 = std::string(String8(trainNameIn).string());
- if (readTrainInfoSuccess) {
- // Keep the old train version if we received an empty version.
- if (trainVersionCodeIn == -1) {
- trainVersionCode = trainInfoOnDisk.trainVersionCode;
- } else if (trainVersionCodeIn != trainInfoOnDisk.trainVersionCode) {
- // Reset experiment ids if we receive a new non-empty train version.
- resetExperimentIds = true;
- }
-
- // Keep the old train name if we received an empty train name.
- if (trainNameUtf8.size() == 0) {
- trainNameUtf8 = trainInfoOnDisk.trainName;
- } else if (trainNameUtf8 != trainInfoOnDisk.trainName) {
- // Reset experiment ids if we received a new valid train name.
- resetExperimentIds = true;
- }
-
- // Reset if we received a different experiment id.
- if (!experimentIdsIn.empty() &&
- (trainInfoOnDisk.experimentIds.empty() ||
- experimentIdsIn[0] != trainInfoOnDisk.experimentIds[0])) {
- resetExperimentIds = true;
- }
- }
-
- // Find the right experiment IDs
- std::vector<int64_t> experimentIds;
- if (resetExperimentIds || !readTrainInfoSuccess) {
- experimentIds = experimentIdsIn;
- } else {
- experimentIds = trainInfoOnDisk.experimentIds;
- }
-
- if (!experimentIds.empty()) {
- int64_t firstId = experimentIds[0];
- switch (state) {
- case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALL_SUCCESS:
- experimentIds.push_back(firstId + 1);
- break;
- case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_INITIATED:
- experimentIds.push_back(firstId + 2);
- break;
- case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_SUCCESS:
- experimentIds.push_back(firstId + 3);
- break;
- }
- }
-
- // Flatten the experiment IDs to proto
- vector<uint8_t> experimentIdsProtoBuffer;
- writeExperimentIdsToProto(experimentIds, &experimentIdsProtoBuffer);
- StorageManager::writeTrainInfo(trainVersionCode, trainNameUtf8, state, experimentIds);
-
- userid_t userId = multiuser_get_user_id(uid);
- bool requiresStaging = options & IStatsManager::FLAG_REQUIRE_STAGING;
- bool rollbackEnabled = options & IStatsManager::FLAG_ROLLBACK_ENABLED;
- bool requiresLowLatencyMonitor = options & IStatsManager::FLAG_REQUIRE_LOW_LATENCY_MONITOR;
- LogEvent event(trainNameUtf8, trainVersionCode, requiresStaging, rollbackEnabled,
- requiresLowLatencyMonitor, state, experimentIdsProtoBuffer, userId);
- mProcessor->OnLogEvent(&event);
+Status StatsService::unregisterPullAtomCallback(int32_t uid, int32_t atomTag) {
+ ENFORCE_UID(AID_SYSTEM);
+ VLOG("StatsService::unregisterPullAtomCallback called.");
+ mPullerManager->UnregisterPullAtomCallback(uid, atomTag);
return Status::ok();
}
-Status StatsService::sendWatchdogRollbackOccurredAtom(const int32_t rollbackTypeIn,
- const android::String16& packageNameIn,
- const int64_t packageVersionCodeIn,
- const int32_t rollbackReasonIn,
- const android::String16&
- failingPackageNameIn) {
- // Note: We skip the usage stats op check here since we do not have a package name.
- // This is ok since we are overloading the usage_stats permission.
- // This method only sends data, it does not receive it.
- pid_t pid = IPCThreadState::self()->getCallingPid();
- uid_t uid = IPCThreadState::self()->getCallingUid();
- // Root, system, and shell always have access
- if (uid != AID_ROOT && uid != AID_SYSTEM && uid != AID_SHELL) {
- // Caller must be granted these permissions
- if (!checkCallingPermission(String16(kPermissionDump))) {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
- kPermissionDump));
- }
- if (!checkCallingPermission(String16(kPermissionUsage))) {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
- kPermissionUsage));
- }
- }
-
- android::util::stats_write(android::util::WATCHDOG_ROLLBACK_OCCURRED,
- rollbackTypeIn, String8(packageNameIn).string(), packageVersionCodeIn,
- rollbackReasonIn, String8(failingPackageNameIn).string());
-
- // Fast return to save disk read.
- if (rollbackTypeIn != android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS
- && rollbackTypeIn !=
- android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE) {
- return Status::ok();
+Status StatsService::unregisterNativePullAtomCallback(int32_t atomTag) {
+ if (!checkPermission(kPermissionRegisterPullAtom)) {
+ return exception(
+ EX_SECURITY,
+ StringPrintf("Uid %d does not have the %s permission when unregistering atom %d",
+ AIBinder_getCallingUid(), kPermissionRegisterPullAtom, atomTag));
}
-
- bool readTrainInfoSuccess = false;
- InstallTrainInfo trainInfoOnDisk;
- readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfoOnDisk);
-
- if (!readTrainInfoSuccess) {
- return Status::ok();
- }
- std::vector<int64_t> experimentIds = trainInfoOnDisk.experimentIds;
- if (experimentIds.empty()) {
- return Status::ok();
- }
- switch (rollbackTypeIn) {
- case android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE:
- experimentIds.push_back(experimentIds[0] + 4);
- break;
- case android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS:
- experimentIds.push_back(experimentIds[0] + 5);
- break;
- }
- StorageManager::writeTrainInfo(trainInfoOnDisk.trainVersionCode, trainInfoOnDisk.trainName,
- trainInfoOnDisk.status, experimentIds);
+ VLOG("StatsService::unregisterNativePullAtomCallback called.");
+ int32_t uid = AIBinder_getCallingUid();
+ mPullerManager->UnregisterPullAtomCallback(uid, atomTag);
return Status::ok();
}
Status StatsService::getRegisteredExperimentIds(std::vector<int64_t>* experimentIdsOut) {
- uid_t uid = IPCThreadState::self()->getCallingUid();
-
- // Caller must be granted these permissions
- if (!checkCallingPermission(String16(kPermissionDump))) {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d lacks permission %s", uid, kPermissionDump));
- }
- if (!checkCallingPermission(String16(kPermissionUsage))) {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d lacks permission %s", uid, kPermissionUsage));
- }
+ ENFORCE_UID(AID_SYSTEM);
// TODO: add verifier permission
+ experimentIdsOut->clear();
// Read the latest train info
- InstallTrainInfo trainInfo;
- if (!StorageManager::readTrainInfo(trainInfo)) {
+ vector<InstallTrainInfo> trainInfoList = StorageManager::readAllTrainInfo();
+ if (trainInfoList.empty()) {
// No train info means no experiment IDs, return an empty list
- experimentIdsOut->clear();
return Status::ok();
}
// Copy the experiment IDs to the out vector
- experimentIdsOut->assign(trainInfo.experimentIds.begin(), trainInfo.experimentIds.end());
+ for (InstallTrainInfo& trainInfo : trainInfoList) {
+ experimentIdsOut->insert(experimentIdsOut->end(),
+ trainInfo.experimentIds.begin(),
+ trainInfo.experimentIds.end());
+ }
return Status::ok();
}
-void StatsService::binderDied(const wp <IBinder>& who) {
+void StatsService::statsCompanionServiceDied(void* cookie) {
+ auto thiz = static_cast<StatsService*>(cookie);
+ thiz->statsCompanionServiceDiedImpl();
+}
+
+void StatsService::statsCompanionServiceDiedImpl() {
ALOGW("statscompanion service died");
StatsdStats::getInstance().noteSystemServerRestart(getWallClockSec());
if (mProcessor != nullptr) {
ALOGW("Reset statsd upon system server restarts.");
int64_t systemServerRestartNs = getElapsedRealtimeNs();
- ProtoOutputStream proto;
+ ProtoOutputStream activeConfigsProto;
mProcessor->WriteActiveConfigsToProtoOutputStream(systemServerRestartNs,
- STATSCOMPANION_DIED, &proto);
-
+ STATSCOMPANION_DIED, &activeConfigsProto);
+ metadata::StatsMetadataList metadataList;
+ mProcessor->WriteMetadataToProto(getWallClockNs(),
+ systemServerRestartNs, &metadataList);
mProcessor->WriteDataToDisk(STATSCOMPANION_DIED, FAST);
mProcessor->resetConfigs();
std::string serializedActiveConfigs;
- if (proto.serializeToString(&serializedActiveConfigs)) {
+ if (activeConfigsProto.serializeToString(&serializedActiveConfigs)) {
ActiveConfigList activeConfigs;
if (activeConfigs.ParseFromString(serializedActiveConfigs)) {
mProcessor->SetConfigsActiveState(activeConfigs, systemServerRestartNs);
}
}
+ mProcessor->SetMetadataState(metadataList, getWallClockNs(), systemServerRestartNs);
}
mAnomalyAlarmMonitor->setStatsCompanionService(nullptr);
mPeriodicAlarmMonitor->setStatsCompanionService(nullptr);
- SubscriberReporter::getInstance().setStatsCompanionService(nullptr);
mPullerManager->SetStatsCompanionService(nullptr);
}
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 991a205e111d..324ffbd65e51 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -17,7 +17,14 @@
#ifndef STATS_SERVICE_H
#define STATS_SERVICE_H
+#include <aidl/android/os/BnStatsd.h>
+#include <aidl/android/os/IPendingIntentRef.h>
+#include <aidl/android/os/IPullAtomCallback.h>
#include <gtest/gtest_prod.h>
+#include <utils/Looper.h>
+
+#include <mutex>
+
#include "StatsLogProcessor.h"
#include "anomaly/AlarmMonitor.h"
#include "config/ConfigManager.h"
@@ -26,32 +33,25 @@
#include "packages/UidMap.h"
#include "shell/ShellSubscriber.h"
#include "statscompanion_util.h"
-
-#include <android/frameworks/stats/1.0/IStats.h>
-#include <android/frameworks/stats/1.0/types.h>
-#include <android/os/BnStatsManager.h>
-#include <android/os/IStatsCompanionService.h>
-#include <android/os/IStatsManager.h>
-#include <binder/IResultReceiver.h>
-#include <binder/ParcelFileDescriptor.h>
-#include <utils/Looper.h>
-
-#include <mutex>
+#include "utils/MultiConditionTrigger.h"
using namespace android;
-using namespace android::binder;
-using namespace android::frameworks::stats::V1_0;
using namespace android::os;
using namespace std;
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::os::BnStatsd;
+using aidl::android::os::IPendingIntentRef;
+using aidl::android::os::IPullAtomCallback;
+using ::ndk::ScopedAIBinder_DeathRecipient;
+using ::ndk::ScopedFileDescriptor;
+using std::shared_ptr;
+
namespace android {
namespace os {
namespace statsd {
-using android::hardware::Return;
-
-class StatsService : public BnStatsManager,
- public IBinder::DeathRecipient {
+class StatsService : public BnStatsd {
public:
StatsService(const sp<Looper>& handlerLooper, std::shared_ptr<LogEventQueue> queue);
virtual ~StatsService();
@@ -59,21 +59,21 @@ public:
/** The anomaly alarm registered with AlarmManager won't be updated by less than this. */
const uint32_t MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS = 5;
- virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
- virtual status_t dump(int fd, const Vector<String16>& args);
- virtual status_t command(int inFd, int outFd, int err, Vector<String8>& args,
- sp<IResultReceiver> resultReceiver);
+ virtual status_t dump(int fd, const char** args, uint32_t numArgs) override;
+ virtual status_t handleShellCommand(int in, int out, int err, const char** argv,
+ uint32_t argc) override;
virtual Status systemRunning();
virtual Status statsCompanionReady();
+ virtual Status bootCompleted();
virtual Status informAnomalyAlarmFired();
virtual Status informPollAlarmFired();
virtual Status informAlarmForSubscriberTriggeringFired();
- virtual Status informAllUidData(const ParcelFileDescriptor& fd);
- virtual Status informOnePackage(const String16& app, int32_t uid, int64_t version,
- const String16& version_string, const String16& installer);
- virtual Status informOnePackageRemoved(const String16& app, int32_t uid);
+ virtual Status informAllUidData(const ScopedFileDescriptor& fd);
+ virtual Status informOnePackage(const string& app, int32_t uid, int64_t version,
+ const string& versionString, const string& installer);
+ virtual Status informOnePackageRemoved(const string& app, int32_t uid);
virtual Status informDeviceShutdown();
/**
@@ -95,15 +95,14 @@ public:
* Binder call for clients to request data for this configuration key.
*/
virtual Status getData(int64_t key,
- const String16& packageName,
+ const int32_t callingUid,
vector<uint8_t>* output) override;
/**
* Binder call for clients to get metadata across all configs in statsd.
*/
- virtual Status getMetadata(const String16& packageName,
- vector<uint8_t>* output) override;
+ virtual Status getMetadata(vector<uint8_t>* output) override;
/**
@@ -112,103 +111,92 @@ public:
*/
virtual Status addConfiguration(int64_t key,
const vector<uint8_t>& config,
- const String16& packageName) override;
+ const int32_t callingUid) override;
/**
* Binder call to let clients register the data fetch operation for a configuration.
*/
virtual Status setDataFetchOperation(int64_t key,
- const sp<android::IBinder>& intentSender,
- const String16& packageName) override;
+ const shared_ptr<IPendingIntentRef>& pir,
+ const int32_t callingUid) override;
/**
* Binder call to remove the data fetch operation for the specified config key.
*/
virtual Status removeDataFetchOperation(int64_t key,
- const String16& packageName) override;
+ const int32_t callingUid) override;
/**
* Binder call to let clients register the active configs changed operation.
*/
- virtual Status setActiveConfigsChangedOperation(const sp<android::IBinder>& intentSender,
- const String16& packageName,
+ virtual Status setActiveConfigsChangedOperation(const shared_ptr<IPendingIntentRef>& pir,
+ const int32_t callingUid,
vector<int64_t>* output) override;
/**
* Binder call to remove the active configs changed operation for the specified package..
*/
- virtual Status removeActiveConfigsChangedOperation(const String16& packageName) override;
+ virtual Status removeActiveConfigsChangedOperation(const int32_t callingUid) override;
/**
* Binder call to allow clients to remove the specified configuration.
*/
virtual Status removeConfiguration(int64_t key,
- const String16& packageName) override;
+ const int32_t callingUid) override;
/**
- * Binder call to associate the given config's subscriberId with the given intentSender.
- * intentSender must be convertible into an IntentSender (in Java) using IntentSender(IBinder).
+ * Binder call to associate the given config's subscriberId with the given pendingIntentRef.
*/
virtual Status setBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
- const sp<android::IBinder>& intentSender,
- const String16& packageName) override;
+ const shared_ptr<IPendingIntentRef>& pir,
+ const int32_t callingUid) override;
/**
- * Binder call to unassociate the given config's subscriberId with any intentSender.
+ * Binder call to unassociate the given config's subscriberId with any pendingIntentRef.
*/
virtual Status unsetBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
- const String16& packageName) override;
+ const int32_t callingUid) override;
/** Inform statsCompanion that statsd is ready. */
virtual void sayHiToStatsCompanion();
/**
- * Binder call to get AppBreadcrumbReported atom.
+ * Binder call to notify statsd that all pullers from boot have been registered.
*/
- virtual Status sendAppBreadcrumbAtom(int32_t label, int32_t state) override;
+ virtual Status allPullersFromBootRegistered();
/**
- * Binder call to register a callback function for a vendor pulled atom.
- * Note: this atom must NOT have uid as a field.
+ * Binder call to register a callback function for a pulled atom.
*/
- virtual Status registerPullerCallback(int32_t atomTag,
- const sp<android::os::IStatsPullerCallback>& pullerCallback,
- const String16& packageName) override;
+ virtual Status registerPullAtomCallback(
+ int32_t uid, int32_t atomTag, int64_t coolDownMillis, int64_t timeoutMillis,
+ const std::vector<int32_t>& additiveFields,
+ const shared_ptr<IPullAtomCallback>& pullerCallback) override;
/**
- * Binder call to unregister any existing callback function for a vendor pulled atom.
+ * Binder call to register a callback function for a pulled atom.
*/
- virtual Status unregisterPullerCallback(int32_t atomTag, const String16& packageName) override;
+ virtual Status registerNativePullAtomCallback(
+ int32_t atomTag, int64_t coolDownMillis, int64_t timeoutMillis,
+ const std::vector<int32_t>& additiveFields,
+ const shared_ptr<IPullAtomCallback>& pullerCallback) override;
/**
- * Binder call to log BinaryPushStateChanged atom.
+ * Binder call to unregister any existing callback for the given uid and atom.
*/
- virtual Status sendBinaryPushStateChangedAtom(
- const android::String16& trainNameIn,
- const int64_t trainVersionCodeIn,
- const int options,
- const int32_t state,
- const std::vector<int64_t>& experimentIdsIn) override;
+ virtual Status unregisterPullAtomCallback(int32_t uid, int32_t atomTag) override;
/**
- * Binder call to log WatchdogRollbackOccurred atom.
+ * Binder call to unregister any existing callback for the given atom and calling uid.
*/
- virtual Status sendWatchdogRollbackOccurredAtom(
- const int32_t rollbackTypeIn,
- const android::String16& packageNameIn,
- const int64_t packageVersionCodeIn,
- const int32_t rollbackReasonIn,
- const android::String16& failingPackageNameIn) override;
+ virtual Status unregisterNativePullAtomCallback(int32_t atomTag) override;
/**
* Binder call to get registered experiment IDs.
*/
virtual Status getRegisteredExperimentIds(std::vector<int64_t>* expIdsOut);
- /** IBinder::DeathRecipient */
- virtual void binderDied(const wp<IBinder>& who) override;
-
private:
/**
* Load system properties at init.
@@ -340,6 +328,17 @@ private:
void set_config(int uid, const string& name, const StatsdConfig& config);
/**
+ * Death recipient callback that is called when StatsCompanionService dies.
+ * The cookie is a pointer to a StatsService object.
+ */
+ static void statsCompanionServiceDied(void* cookie);
+
+ /**
+ * Implementation of statsCompanionServiceDied.
+ */
+ void statsCompanionServiceDiedImpl();
+
+ /**
* Tracks the uid <--> package name mapping.
*/
sp<UidMap> mUidMap;
@@ -382,17 +381,27 @@ private:
mutable mutex mShellSubscriberMutex;
std::shared_ptr<LogEventQueue> mEventQueue;
+ MultiConditionTrigger mBootCompleteTrigger;
+ static const inline string kBootCompleteTag = "BOOT_COMPLETE";
+ static const inline string kUidMapReceivedTag = "UID_MAP";
+ static const inline string kAllPullersRegisteredTag = "PULLERS_REGISTERED";
+
+ ScopedAIBinder_DeathRecipient mStatsCompanionServiceDeathRecipient;
+
FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
FRIEND_TEST(StatsServiceTest, TestAddConfig_simple);
FRIEND_TEST(StatsServiceTest, TestAddConfig_empty);
FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid);
FRIEND_TEST(StatsServiceTest, TestGetUidFromArgs);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp);
+ FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnBoot);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit);
+ FRIEND_TEST(PartialBucketE2eTest, TestValueMetricOnBootWithoutMinPartialBucket);
FRIEND_TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket);
FRIEND_TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket);
+ FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricOnBootWithoutMinPartialBucket);
FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket);
FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket);
};
diff --git a/cmds/statsd/src/external/PowerStatsPuller.h b/cmds/statsd/src/annotations.h
index 6f15bd68fa94..cf7f5433663f 100644
--- a/cmds/statsd/src/external/PowerStatsPuller.h
+++ b/cmds/statsd/src/annotations.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -16,23 +16,18 @@
#pragma once
-#include "StatsPuller.h"
-
namespace android {
namespace os {
namespace statsd {
-/**
- * Reads hal for power.stats
- */
-class PowerStatsPuller : public StatsPuller {
-public:
- PowerStatsPuller();
-
-private:
- bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
-};
+const uint8_t ANNOTATION_ID_IS_UID = 1;
+const uint8_t ANNOTATION_ID_TRUNCATE_TIMESTAMP = 2;
+const uint8_t ANNOTATION_ID_PRIMARY_FIELD = 3;
+const uint8_t ANNOTATION_ID_EXCLUSIVE_STATE = 4;
+const uint8_t ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID = 5;
+const uint8_t ANNOTATION_ID_TRIGGER_STATE_RESET = 7;
+const uint8_t ANNOTATION_ID_STATE_NESTED = 8;
-} // namespace statsd
-} // namespace os
-} // namespace android
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/anomaly/AlarmMonitor.cpp b/cmds/statsd/src/anomaly/AlarmMonitor.cpp
index bc36dadacddb..b632d040eb43 100644
--- a/cmds/statsd/src/anomaly/AlarmMonitor.cpp
+++ b/cmds/statsd/src/anomaly/AlarmMonitor.cpp
@@ -26,17 +26,19 @@ namespace statsd {
AlarmMonitor::AlarmMonitor(
uint32_t minDiffToUpdateRegisteredAlarmTimeSec,
- const std::function<void(const sp<IStatsCompanionService>&, int64_t)>& updateAlarm,
- const std::function<void(const sp<IStatsCompanionService>&)>& cancelAlarm)
- : mRegisteredAlarmTimeSec(0), mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec),
+ const std::function<void(const shared_ptr<IStatsCompanionService>&, int64_t)>& updateAlarm,
+ const std::function<void(const shared_ptr<IStatsCompanionService>&)>& cancelAlarm)
+ : mRegisteredAlarmTimeSec(0),
+ mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec),
mUpdateAlarm(updateAlarm),
mCancelAlarm(cancelAlarm) {}
AlarmMonitor::~AlarmMonitor() {}
-void AlarmMonitor::setStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) {
+void AlarmMonitor::setStatsCompanionService(
+ shared_ptr<IStatsCompanionService> statsCompanionService) {
std::lock_guard<std::mutex> lock(mLock);
- sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
+ shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService;
mStatsCompanionService = statsCompanionService;
if (statsCompanionService == nullptr) {
VLOG("Erasing link to statsCompanionService");
diff --git a/cmds/statsd/src/anomaly/AlarmMonitor.h b/cmds/statsd/src/anomaly/AlarmMonitor.h
index 219695ef5e43..5c34e381ba0b 100644
--- a/cmds/statsd/src/anomaly/AlarmMonitor.h
+++ b/cmds/statsd/src/anomaly/AlarmMonitor.h
@@ -18,7 +18,7 @@
#include "anomaly/indexed_priority_queue.h"
-#include <android/os/IStatsCompanionService.h>
+#include <aidl/android/os/IStatsCompanionService.h>
#include <utils/RefBase.h>
#include <unordered_set>
@@ -26,7 +26,9 @@
using namespace android;
-using android::os::IStatsCompanionService;
+using aidl::android::os::IStatsCompanionService;
+using std::function;
+using std::shared_ptr;
using std::unordered_set;
namespace android {
@@ -64,8 +66,9 @@ public:
* alarm.
*/
AlarmMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec,
- const std::function<void(const sp<IStatsCompanionService>&, int64_t)>& updateAlarm,
- const std::function<void(const sp<IStatsCompanionService>&)>& cancelAlarm);
+ const function<void(const shared_ptr<IStatsCompanionService>&, int64_t)>&
+ updateAlarm,
+ const function<void(const shared_ptr<IStatsCompanionService>&)>& cancelAlarm);
~AlarmMonitor();
/**
@@ -74,7 +77,7 @@ public:
* If nullptr, AnomalyMonitor will continue to add/remove alarms, but won't
* update IStatsCompanionService (until such time as it is set non-null).
*/
- void setStatsCompanionService(sp<IStatsCompanionService> statsCompanionService);
+ void setStatsCompanionService(shared_ptr<IStatsCompanionService> statsCompanionService);
/**
* Adds the given alarm (reference) to the queue.
@@ -123,7 +126,7 @@ private:
/**
* Binder interface for communicating with StatsCompanionService.
*/
- sp<IStatsCompanionService> mStatsCompanionService = nullptr;
+ shared_ptr<IStatsCompanionService> mStatsCompanionService = nullptr;
/**
* Amount by which the soonest projected alarm must differ from
@@ -147,10 +150,10 @@ private:
int64_t secToMs(uint32_t timeSec);
// Callback function to update the alarm via StatsCompanionService.
- std::function<void(const sp<IStatsCompanionService>, int64_t)> mUpdateAlarm;
+ std::function<void(const shared_ptr<IStatsCompanionService>, int64_t)> mUpdateAlarm;
// Callback function to cancel the alarm via StatsCompanionService.
- std::function<void(const sp<IStatsCompanionService>)> mCancelAlarm;
+ std::function<void(const shared_ptr<IStatsCompanionService>)> mCancelAlarm;
};
diff --git a/cmds/statsd/src/anomaly/AlarmTracker.cpp b/cmds/statsd/src/anomaly/AlarmTracker.cpp
index a21327b4aae0..6d9beb8f718d 100644
--- a/cmds/statsd/src/anomaly/AlarmTracker.cpp
+++ b/cmds/statsd/src/anomaly/AlarmTracker.cpp
@@ -23,7 +23,6 @@
#include "stats_util.h"
#include "storage/StorageManager.h"
-#include <statslog.h>
#include <time.h>
namespace android {
diff --git a/cmds/statsd/src/anomaly/AlarmTracker.h b/cmds/statsd/src/anomaly/AlarmTracker.h
index 2fb3e3b3e26a..2da4a18682ae 100644
--- a/cmds/statsd/src/anomaly/AlarmTracker.h
+++ b/cmds/statsd/src/anomaly/AlarmTracker.h
@@ -22,12 +22,9 @@
#include "config/ConfigKey.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alarm
-#include <android/os/IStatsCompanionService.h>
#include <stdlib.h>
#include <utils/RefBase.h>
-using android::os::IStatsCompanionService;
-
namespace android {
namespace os {
namespace statsd {
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index 7ace44eef564..619752c7c44a 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -18,14 +18,16 @@
#include "Log.h"
#include "AnomalyTracker.h"
-#include "subscriber_util.h"
#include "external/Perfetto.h"
#include "guardrail/StatsdStats.h"
+#include "metadata_util.h"
+#include "stats_log_util.h"
+#include "subscriber_util.h"
#include "subscriber/IncidentdReporter.h"
#include "subscriber/SubscriberReporter.h"
#include <inttypes.h>
-#include <statslog.h>
+#include <statslog_statsd.h>
#include <time.h>
namespace android {
@@ -235,8 +237,8 @@ void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, int64_t metricId
StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id());
// TODO(b/110564268): This should also take in the const MetricDimensionKey& key?
- android::util::stats_write(android::util::ANOMALY_DETECTED, mConfigKey.GetUid(),
- mConfigKey.GetId(), mAlert.id());
+ util::stats_write(util::ANOMALY_DETECTED, mConfigKey.GetUid(),
+ mConfigKey.GetId(), mAlert.id());
}
void AnomalyTracker::detectAndDeclareAnomaly(const int64_t& timestampNs,
@@ -262,6 +264,58 @@ void AnomalyTracker::informSubscribers(const MetricDimensionKey& key, int64_t me
triggerSubscribers(mAlert.id(), metric_id, key, metricValue, mConfigKey, mSubscriptions);
}
+bool AnomalyTracker::writeAlertMetadataToProto(int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs,
+ metadata::AlertMetadata* alertMetadata) {
+ bool metadataWritten = false;
+
+ if (mRefractoryPeriodEndsSec.empty()) {
+ return false;
+ }
+
+ for (const auto& it: mRefractoryPeriodEndsSec) {
+ // Do not write the timestamp to disk if it has already expired
+ if (it.second < systemElapsedTimeNs / NS_PER_SEC) {
+ continue;
+ }
+
+ metadataWritten = true;
+ if (alertMetadata->alert_dim_keyed_data_size() == 0) {
+ alertMetadata->set_alert_id(mAlert.id());
+ }
+
+ metadata::AlertDimensionKeyedData* keyedData = alertMetadata->add_alert_dim_keyed_data();
+ // We convert and write the refractory_end_sec to wall clock time because we do not know
+ // when statsd will start again.
+ int32_t refractoryEndWallClockSec = (int32_t) ((currentWallClockTimeNs / NS_PER_SEC) +
+ (it.second - systemElapsedTimeNs / NS_PER_SEC));
+
+ keyedData->set_last_refractory_ends_sec(refractoryEndWallClockSec);
+ writeMetricDimensionKeyToMetadataDimensionKey(
+ it.first, keyedData->mutable_dimension_key());
+ }
+
+ return metadataWritten;
+}
+
+void AnomalyTracker::loadAlertMetadata(
+ const metadata::AlertMetadata& alertMetadata,
+ int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs) {
+ for (const metadata::AlertDimensionKeyedData& keyedData :
+ alertMetadata.alert_dim_keyed_data()) {
+ if ((uint64_t) keyedData.last_refractory_ends_sec() < currentWallClockTimeNs / NS_PER_SEC) {
+ // Do not update the timestamp if it has already expired.
+ continue;
+ }
+ MetricDimensionKey metricKey = loadMetricDimensionKeyFromProto(
+ keyedData.dimension_key());
+ int32_t refractoryPeriodEndsSec = (int32_t) keyedData.last_refractory_ends_sec() -
+ currentWallClockTimeNs / NS_PER_SEC + systemElapsedTimeNs / NS_PER_SEC;
+ mRefractoryPeriodEndsSec[metricKey] = refractoryPeriodEndsSec;
+ }
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h
index 794ee988ef55..bf36a3bc8990 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.h
@@ -24,6 +24,7 @@
#include "AlarmMonitor.h"
#include "config/ConfigKey.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert
+#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h" // AlertMetadata
#include "stats_util.h" // HashableDimensionKey and DimToValMap
namespace android {
@@ -112,6 +113,17 @@ public:
return; // The base AnomalyTracker class doesn't have alarms.
}
+ // Writes metadata of the alert (refractory_period_end_sec) to AlertMetadata.
+ // Returns true if at least one element is written to alertMetadata.
+ bool writeAlertMetadataToProto(
+ int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs, metadata::AlertMetadata* alertMetadata);
+
+ void loadAlertMetadata(
+ const metadata::AlertMetadata& alertMetadata,
+ int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs);
+
protected:
// For testing only.
// Returns the alarm timestamp in seconds for the query dimension if it exists. Otherwise
diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto
index 16c936c41559..ff5717e4fa78 100644
--- a/cmds/statsd/src/atom_field_options.proto
+++ b/cmds/statsd/src/atom_field_options.proto
@@ -23,45 +23,72 @@ option java_outer_classname = "AtomFieldOptions";
import "google/protobuf/descriptor.proto";
-enum StateField {
- // Default value for fields that are not primary or exclusive state.
- STATE_FIELD_UNSET = 0;
- // Fields that represent the key that the state belongs to.
- PRIMARY = 1;
- // The field that represents the state. It's an exclusive state.
- EXCLUSIVE = 2;
-}
-
-// Used to annotate an atom that reprsents a state change. A state change atom must have exactly ONE
-// exclusive state field, and any number of primary key fields.
-// For example,
-// message UidProcessStateChanged {
-// optional int32 uid = 1 [(state_field_option).option = PRIMARY];
-// optional android.app.ProcessStateEnum state = 2 [(state_field_option).option = EXCLUSIVE];
+// Used to annotate an atom that represents a state change. A state change atom must have exactly
+// ONE exclusive state field, and any number of primary key fields. For example, message
+// UidProcessStateChanged {
+// optional int32 uid = 1 [(state_field_option).primary_field = true];
+// optional android.app.ProcessStateEnum state =
+// 2 [(state_field_option).exclusive_state = true];
// }
-// Each of this UidProcessStateChanged atom represents a state change for a specific uid.
+// Each UidProcessStateChanged atom event represents a state change for a specific uid.
// A new state automatically overrides the previous state.
//
-// If the atom has 2 or more primary fields, it means the combination of the primary fields are
-// the primary key.
+// If the atom has 2 or more primary fields, it means the combination of the
+// primary fields are the primary key.
// For example:
// message ThreadStateChanged {
-// optional int32 pid = 1 [(state_field_option).option = PRIMARY];
-// optional int32 tid = 2 [(state_field_option).option = PRIMARY];
-// optional int32 state = 3 [(state_field_option).option = EXCLUSIVE];
+// optional int32 pid = 1 [(state_field_option).primary_field = true];
+// optional int32 tid = 2 [(state_field_option).primary_field = true];
+// optional int32 state = 3 [(state_field_option).exclusive_state = true];
// }
//
// Sometimes, there is no primary key field, when the state is GLOBAL.
// For example,
-//
// message ScreenStateChanged {
-// optional android.view.DisplayStateEnum state = 1 [(state_field_option).option = EXCLUSIVE];
+// optional android.view.DisplayStateEnum state =
+// 1 [(state_field_option).exclusive_state = true];
// }
//
-// Only fields of primary types can be annotated. AttributionNode cannot be primary keys (and they
-// usually are not).
+// For state atoms with attribution chain, sometimes the primary key is the first uid in the chain.
+// For example:
+// message AudioStateChanged {
+// repeated AttributionNode attribution_node = 1
+// [(stateFieldOption).primary_field_first_uid = true];
+//
+// enum State {
+// OFF = 0;
+// ON = 1;
+// // RESET indicates all audio stopped. Used when it (re)starts (e.g. after it crashes).
+// RESET = 2;
+// }
+// optional State state = 2 [(stateFieldOption).exclusive_state = true];
+// }
message StateAtomFieldOption {
- optional StateField option = 1 [default = STATE_FIELD_UNSET];
+ // Fields that represent the key that the state belongs to.
+ // Used on simple proto fields. Do not use on attribution chains.
+ optional bool primary_field = 1 [default = false];
+
+ // The field that represents the state. It's an exclusive state.
+ optional bool exclusive_state = 2 [default = false];
+
+ // Used on an attribution chain field to indicate that the first uid is the
+ // primary field.
+ optional bool primary_field_first_uid = 3 [default = false];
+
+ // Note: We cannot annotate directly on the enums because many enums are imported from other
+ // proto files in the platform. proto-lite cc library does not support annotations unfortunately
+
+ // Knowing the default state value allows state trackers to remove entries that become the
+ // default state. If there is no default value specified, the default value is unknown, and all
+ // states will be tracked in memory.
+ optional int32 default_state_value = 4;
+
+ // A reset state signals all states go to default value. For example, BLE reset means all active
+ // BLE scans are to be turned off.
+ optional int32 trigger_state_reset_value = 5;
+
+ // If the state change needs to count nesting.
+ optional bool nested = 6 [default = true];
}
// Used to generate StatsLog.write APIs.
@@ -83,7 +110,7 @@ extend google.protobuf.FieldOptions {
optional LogMode log_mode = 50002 [default = MODE_AUTOMATIC];
- optional bool allow_from_any_uid = 50003 [default = false];
+ repeated string module = 50004;
- optional string log_from_module = 50004;
-} \ No newline at end of file
+ optional bool truncate_timestamp = 50005 [default = false];
+}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index b8de3f0eff75..ab1d3cbd232f 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -24,6 +24,8 @@ import "frameworks/base/cmds/statsd/src/atom_field_options.proto";
import "frameworks/base/core/proto/android/app/enums.proto";
import "frameworks/base/core/proto/android/app/job/enums.proto";
import "frameworks/base/core/proto/android/app/settings_enums.proto";
+import "frameworks/base/core/proto/android/app/media_output_enum.proto";
+import "frameworks/base/core/proto/android/app/tvsettings_enums.proto";
import "frameworks/base/core/proto/android/bluetooth/a2dp/enums.proto";
import "frameworks/base/core/proto/android/bluetooth/enums.proto";
import "frameworks/base/core/proto/android/bluetooth/hci/enums.proto";
@@ -46,18 +48,22 @@ import "frameworks/base/core/proto/android/stats/dnsresolver/dns_resolver.proto"
import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy.proto";
import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy_enums.proto";
import "frameworks/base/core/proto/android/stats/docsui/docsui_enums.proto";
+import "frameworks/base/core/proto/android/stats/accessibility/accessibility_enums.proto";
import "frameworks/base/core/proto/android/stats/enums.proto";
import "frameworks/base/core/proto/android/stats/intelligence/enums.proto";
import "frameworks/base/core/proto/android/stats/launcher/launcher.proto";
import "frameworks/base/core/proto/android/stats/location/location_enums.proto";
import "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.proto";
-import "frameworks/base/core/proto/android/stats/otaupdate/updateengine_enums.proto";
+import "frameworks/base/core/proto/android/stats/mediaprovider/mediaprovider_enums.proto";
import "frameworks/base/core/proto/android/stats/storage/storage_enums.proto";
import "frameworks/base/core/proto/android/stats/style/style_enums.proto";
+import "frameworks/base/core/proto/android/stats/sysui/notification_enums.proto";
import "frameworks/base/core/proto/android/telecomm/enums.proto";
import "frameworks/base/core/proto/android/telephony/enums.proto";
import "frameworks/base/core/proto/android/view/enums.proto";
import "frameworks/base/core/proto/android/wifi/enums.proto";
+import "frameworks/base/core/proto/android/stats/textclassifier/textclassifier_enums.proto";
+import "frameworks/base/core/proto/android/stats/otaupdate/updateengine_enums.proto";
/**
* The primary atom class. This message defines all of the available
@@ -76,241 +82,258 @@ message Atom {
// Pushed atoms start at 2.
oneof pushed {
// For StatsLog reasons, 1 is illegal and will not work. Must start at 2.
- BleScanStateChanged ble_scan_state_changed = 2 [(log_from_module) = "bluetooth"];
- ProcessStateChanged process_state_changed = 3;
- BleScanResultReceived ble_scan_result_received = 4 [(log_from_module) = "bluetooth"];
- SensorStateChanged sensor_state_changed = 5;
- GpsScanStateChanged gps_scan_state_changed = 6;
- SyncStateChanged sync_state_changed = 7;
- ScheduledJobStateChanged scheduled_job_state_changed = 8;
- ScreenBrightnessChanged screen_brightness_changed = 9;
- WakelockStateChanged wakelock_state_changed = 10;
- LongPartialWakelockStateChanged long_partial_wakelock_state_changed = 11;
- MobileRadioPowerStateChanged mobile_radio_power_state_changed = 12 [(log_from_module) = "framework"];
- WifiRadioPowerStateChanged wifi_radio_power_state_changed = 13 [(log_from_module) = "framework"];
- ActivityManagerSleepStateChanged activity_manager_sleep_state_changed = 14;
- MemoryFactorStateChanged memory_factor_state_changed = 15;
- ExcessiveCpuUsageReported excessive_cpu_usage_reported = 16;
- CachedKillReported cached_kill_reported = 17;
- ProcessMemoryStatReported process_memory_stat_reported = 18;
- LauncherUIChanged launcher_event = 19;
- BatterySaverModeStateChanged battery_saver_mode_state_changed = 20;
- DeviceIdleModeStateChanged device_idle_mode_state_changed = 21;
- DeviceIdlingModeStateChanged device_idling_mode_state_changed = 22;
- AudioStateChanged audio_state_changed = 23;
- MediaCodecStateChanged media_codec_state_changed = 24;
- CameraStateChanged camera_state_changed = 25;
- FlashlightStateChanged flashlight_state_changed = 26;
- UidProcessStateChanged uid_process_state_changed = 27;
- ProcessLifeCycleStateChanged process_life_cycle_state_changed = 28;
- ScreenStateChanged screen_state_changed = 29;
- BatteryLevelChanged battery_level_changed = 30;
- ChargingStateChanged charging_state_changed = 31;
- PluggedStateChanged plugged_state_changed = 32;
- InteractiveStateChanged interactive_state_changed = 33;
+ BleScanStateChanged ble_scan_state_changed = 2
+ [(module) = "bluetooth", (module) = "statsdtest"];
+ ProcessStateChanged process_state_changed = 3 [(module) = "framework"];
+ BleScanResultReceived ble_scan_result_received = 4 [(module) = "bluetooth"];
+ SensorStateChanged sensor_state_changed =
+ 5 [(module) = "framework", (module) = "statsdtest"];
+ GpsScanStateChanged gps_scan_state_changed = 6 [(module) = "framework"];
+ SyncStateChanged sync_state_changed = 7 [(module) = "framework", (module) = "statsdtest"];
+ ScheduledJobStateChanged scheduled_job_state_changed =
+ 8 [(module) = "framework", (module) = "statsdtest"];
+ ScreenBrightnessChanged screen_brightness_changed =
+ 9 [(module) = "framework", (module) = "statsdtest"];
+ WakelockStateChanged wakelock_state_changed =
+ 10 [(module) = "framework", (module) = "statsdtest"];
+ LongPartialWakelockStateChanged long_partial_wakelock_state_changed =
+ 11 [(module) = "framework"];
+ MobileRadioPowerStateChanged mobile_radio_power_state_changed =
+ 12 [(module) = "framework", (truncate_timestamp) = true];
+ WifiRadioPowerStateChanged wifi_radio_power_state_changed = 13 [(module) = "framework"];
+ ActivityManagerSleepStateChanged activity_manager_sleep_state_changed =
+ 14 [(module) = "framework"];
+ MemoryFactorStateChanged memory_factor_state_changed = 15 [(module) = "framework"];
+ ExcessiveCpuUsageReported excessive_cpu_usage_reported = 16 [(module) = "framework"];
+ CachedKillReported cached_kill_reported = 17 [(module) = "framework"];
+ ProcessMemoryStatReported process_memory_stat_reported = 18 [(module) = "framework"];
+ LauncherUIChanged launcher_event = 19 [(module) = "sysui"];
+ BatterySaverModeStateChanged battery_saver_mode_state_changed =
+ 20 [(module) = "framework", (module) = "statsdtest"];
+ DeviceIdleModeStateChanged device_idle_mode_state_changed = 21 [(module) = "framework"];
+ DeviceIdlingModeStateChanged device_idling_mode_state_changed = 22 [(module) = "framework"];
+ AudioStateChanged audio_state_changed =
+ 23 [(module) = "framework", (truncate_timestamp) = true];
+ MediaCodecStateChanged media_codec_state_changed = 24 [(module) = "framework"];
+ CameraStateChanged camera_state_changed = 25 [(module) = "framework"];
+ FlashlightStateChanged flashlight_state_changed = 26 [(module) = "framework"];
+ UidProcessStateChanged uid_process_state_changed =
+ 27 [(module) = "framework", (module) = "statsdtest"];
+ ProcessLifeCycleStateChanged process_life_cycle_state_changed =
+ 28 [(module) = "framework", (module) = "statsdtest"];
+ ScreenStateChanged screen_state_changed =
+ 29 [(module) = "framework", (module) = "statsdtest"];
+ BatteryLevelChanged battery_level_changed =
+ 30 [(module) = "framework", (module) = "statsdtest"];
+ ChargingStateChanged charging_state_changed = 31 [(module) = "framework"];
+ PluggedStateChanged plugged_state_changed = 32
+ [(module) = "framework", (module) = "statsdtest"];
+ InteractiveStateChanged interactive_state_changed = 33 [(module) = "framework"];
TouchEventReported touch_event_reported = 34;
- WakeupAlarmOccurred wakeup_alarm_occurred = 35;
- KernelWakeupReported kernel_wakeup_reported = 36;
- WifiLockStateChanged wifi_lock_state_changed = 37;
- WifiSignalStrengthChanged wifi_signal_strength_changed = 38;
- WifiScanStateChanged wifi_scan_state_changed = 39;
- PhoneSignalStrengthChanged phone_signal_strength_changed = 40;
- SettingChanged setting_changed = 41;
- ActivityForegroundStateChanged activity_foreground_state_changed = 42;
- IsolatedUidChanged isolated_uid_changed = 43;
- PacketWakeupOccurred packet_wakeup_occurred = 44 [(log_from_module) = "framework"];
- WallClockTimeShifted wall_clock_time_shifted = 45;
- AnomalyDetected anomaly_detected = 46;
- AppBreadcrumbReported app_breadcrumb_reported = 47 [(allow_from_any_uid) = true];
- AppStartOccurred app_start_occurred = 48;
- AppStartCanceled app_start_canceled = 49;
- AppStartFullyDrawn app_start_fully_drawn = 50;
- LmkKillOccurred lmk_kill_occurred = 51 [(log_from_module) = "lmkd"];
- PictureInPictureStateChanged picture_in_picture_state_changed = 52;
- WifiMulticastLockStateChanged wifi_multicast_lock_state_changed = 53;
- LmkStateChanged lmk_state_changed = 54 [(log_from_module) = "lmkd"];
- AppStartMemoryStateCaptured app_start_memory_state_captured = 55;
- ShutdownSequenceReported shutdown_sequence_reported = 56;
+ WakeupAlarmOccurred wakeup_alarm_occurred = 35 [(module) = "framework"];
+ KernelWakeupReported kernel_wakeup_reported = 36 [(module) = "framework"];
+ WifiLockStateChanged wifi_lock_state_changed = 37 [(module) = "wifi"];
+ WifiSignalStrengthChanged wifi_signal_strength_changed = 38 [(module) = "wifi"];
+ WifiScanStateChanged wifi_scan_state_changed = 39 [(module) = "wifi"];
+ PhoneSignalStrengthChanged phone_signal_strength_changed =
+ 40 [(module) = "framework", (truncate_timestamp) = true];
+ SettingChanged setting_changed = 41 [(module) = "framework"];
+ ActivityForegroundStateChanged activity_foreground_state_changed =
+ 42 [(module) = "framework", (module) = "statsdtest"];
+ IsolatedUidChanged isolated_uid_changed =
+ 43 [(module) = "framework", (module) = "statsd", (module) = "statsdtest"];
+ PacketWakeupOccurred packet_wakeup_occurred = 44 [(module) = "framework"];
+ WallClockTimeShifted wall_clock_time_shifted = 45 [(module) = "framework"];
+ AnomalyDetected anomaly_detected = 46 [(module) = "statsd"];
+ AppBreadcrumbReported app_breadcrumb_reported = 47 [(module) = "statsd"];
+ AppStartOccurred app_start_occurred = 48 [(module) = "framework", (module) = "statsdtest"];
+ AppStartCanceled app_start_canceled = 49 [(module) = "framework"];
+ AppStartFullyDrawn app_start_fully_drawn = 50 [(module) = "framework"];
+ LmkKillOccurred lmk_kill_occurred = 51 [(module) = "lmkd"];
+ PictureInPictureStateChanged picture_in_picture_state_changed = 52 [(module) = "framework"];
+ WifiMulticastLockStateChanged wifi_multicast_lock_state_changed = 53 [(module) = "wifi"];
+ LmkStateChanged lmk_state_changed = 54 [(module) = "lmkd"];
+ AppStartMemoryStateCaptured app_start_memory_state_captured = 55 [(module) = "framework"];
+ ShutdownSequenceReported shutdown_sequence_reported = 56 [(module) = "framework"];
BootSequenceReported boot_sequence_reported = 57;
- DaveyOccurred davey_occurred = 58 [(allow_from_any_uid) = true];
- OverlayStateChanged overlay_state_changed = 59;
- ForegroundServiceStateChanged foreground_service_state_changed = 60;
- CallStateChanged call_state_changed = 61;
- KeyguardStateChanged keyguard_state_changed = 62;
- KeyguardBouncerStateChanged keyguard_bouncer_state_changed = 63;
- KeyguardBouncerPasswordEntered keyguard_bouncer_password_entered = 64;
- AppDied app_died = 65;
- ResourceConfigurationChanged resource_configuration_changed = 66;
- BluetoothEnabledStateChanged bluetooth_enabled_state_changed = 67;
+ DaveyOccurred davey_occurred = 58 [(module) = "statsd"];
+ OverlayStateChanged overlay_state_changed =
+ 59 [(module) = "framework", (module) = "statsdtest"];
+ ForegroundServiceStateChanged foreground_service_state_changed
+ = 60 [(module) = "framework"];
+ CallStateChanged call_state_changed =
+ 61 [(module) = "telecom", (truncate_timestamp) = true];
+ KeyguardStateChanged keyguard_state_changed = 62 [(module) = "sysui"];
+ KeyguardBouncerStateChanged keyguard_bouncer_state_changed = 63 [(module) = "sysui"];
+ KeyguardBouncerPasswordEntered keyguard_bouncer_password_entered = 64 [(module) = "sysui"];
+ AppDied app_died = 65 [(module) = "framework"];
+ ResourceConfigurationChanged resource_configuration_changed = 66 [(module) = "framework"];
+ BluetoothEnabledStateChanged bluetooth_enabled_state_changed = 67 [(module) = "framework"];
BluetoothConnectionStateChanged bluetooth_connection_state_changed =
- 68 [(log_from_module) = "bluetooth"];
- GpsSignalQualityChanged gps_signal_quality_changed = 69;
- UsbConnectorStateChanged usb_connector_state_changed = 70;
+ 68 [(module) = "bluetooth"];
+ GpsSignalQualityChanged gps_signal_quality_changed = 69 [(module) = "framework"];
+ UsbConnectorStateChanged usb_connector_state_changed = 70 [(module) = "framework"];
SpeakerImpedanceReported speaker_impedance_reported = 71;
HardwareFailed hardware_failed = 72;
PhysicalDropDetected physical_drop_detected = 73;
ChargeCyclesReported charge_cycles_reported = 74;
- MobileConnectionStateChanged mobile_connection_state_changed =
- 75 [(log_from_module) = "telephony"];
- MobileRadioTechnologyChanged mobile_radio_technology_changed =
- 76 [(log_from_module) = "telephony"];
- UsbDeviceAttached usb_device_attached = 77;
- AppCrashOccurred app_crash_occurred = 78;
- ANROccurred anr_occurred = 79;
- WTFOccurred wtf_occurred = 80;
- LowMemReported low_mem_reported = 81;
+ MobileConnectionStateChanged mobile_connection_state_changed = 75 [(module) = "telephony"];
+ MobileRadioTechnologyChanged mobile_radio_technology_changed = 76 [(module) = "telephony"];
+ UsbDeviceAttached usb_device_attached = 77 [(module) = "framework"];
+ AppCrashOccurred app_crash_occurred = 78 [(module) = "framework", (module) = "statsdtest"];
+ ANROccurred anr_occurred = 79 [(module) = "framework"];
+ WTFOccurred wtf_occurred = 80 [(module) = "framework"];
+ LowMemReported low_mem_reported = 81 [(module) = "framework"];
GenericAtom generic_atom = 82;
- KeyValuePairsAtom key_value_pairs_atom = 83 [(allow_from_any_uid) = true];
- VibratorStateChanged vibrator_state_changed = 84;
- DeferredJobStatsReported deferred_job_stats_reported = 85;
+ KeyValuePairsAtom key_value_pairs_atom = 83 [(module) = "framework", (module) = "statsd"];
+ VibratorStateChanged vibrator_state_changed = 84 [(module) = "framework"];
+ DeferredJobStatsReported deferred_job_stats_reported = 85 [(module) = "framework"];
ThermalThrottlingStateChanged thermal_throttling = 86 [deprecated=true];
- BiometricAcquired biometric_acquired = 87;
- BiometricAuthenticated biometric_authenticated = 88;
- BiometricErrorOccurred biometric_error_occurred = 89;
- // Atom number 90 is available for use.
+ BiometricAcquired biometric_acquired = 87 [(module) = "framework"];
+ BiometricAuthenticated biometric_authenticated = 88 [(module) = "framework"];
+ BiometricErrorOccurred biometric_error_occurred = 89 [(module) = "framework"];
+ UiEventReported ui_event_reported = 90 [(module) = "framework", (module) = "sysui"];
BatteryHealthSnapshot battery_health_snapshot = 91;
SlowIo slow_io = 92;
BatteryCausedShutdown battery_caused_shutdown = 93;
- PhoneServiceStateChanged phone_service_state_changed = 94;
- PhoneStateChanged phone_state_changed = 95;
+ PhoneServiceStateChanged phone_service_state_changed = 94 [(module) = "framework"];
+ PhoneStateChanged phone_state_changed = 95 [(module) = "framework"];
UserRestrictionChanged user_restriction_changed = 96;
- SettingsUIChanged settings_ui_changed = 97;
- ConnectivityStateChanged connectivity_state_changed = 98;
+ SettingsUIChanged settings_ui_changed = 97 [(module) = "settings"];
+ ConnectivityStateChanged connectivity_state_changed = 98 [(module) = "framework"];
// TODO: service state change is very noisy shortly after boot, as well
// as at other transitions - coming out of doze, device plugged in, etc.
// Consider removing this if it becomes a problem
- ServiceStateChanged service_state_changed = 99;
- ServiceLaunchReported service_launch_reported = 100;
- FlagFlipUpdateOccurred flag_flip_update_occurred = 101;
- BinaryPushStateChanged binary_push_state_changed = 102;
- DevicePolicyEvent device_policy_event = 103;
- DocsUIFileOperationCanceledReported docs_ui_file_op_canceled =
- 104 [(log_from_module) = "docsui"];
- DocsUIFileOperationCopyMoveModeReported
- docs_ui_file_op_copy_move_mode_reported =
- 105 [(log_from_module) = "docsui"];
- DocsUIFileOperationFailureReported docs_ui_file_op_failure =
- 106 [(log_from_module) = "docsui"];
- DocsUIFileOperationReported docs_ui_provider_file_op =
- 107 [(log_from_module) = "docsui"];
- DocsUIInvalidScopedAccessRequestReported
- docs_ui_invalid_scoped_access_request =
- 108 [(log_from_module) = "docsui"];
- DocsUILaunchReported docs_ui_launch_reported =
- 109 [(log_from_module) = "docsui"];
- DocsUIRootVisitedReported docs_ui_root_visited =
- 110 [(log_from_module) = "docsui"];
- DocsUIStartupMsReported docs_ui_startup_ms =
- 111 [(log_from_module) = "docsui"];
- DocsUIUserActionReported docs_ui_user_action_reported =
- 112 [(log_from_module) = "docsui"];
- WifiEnabledStateChanged wifi_enabled_state_changed = 113;
- WifiRunningStateChanged wifi_running_state_changed = 114;
- AppCompacted app_compacted = 115;
- NetworkDnsEventReported network_dns_event_reported = 116 [(log_from_module) = "resolv"];
+ ServiceStateChanged service_state_changed = 99 [(module) = "framework"];
+ ServiceLaunchReported service_launch_reported = 100 [(module) = "framework"];
+ FlagFlipUpdateOccurred flag_flip_update_occurred = 101 [(module) = "framework"];
+ BinaryPushStateChanged binary_push_state_changed = 102 [(module) = "statsd"];
+ DevicePolicyEvent device_policy_event = 103 [(module) = "framework"];
+ DocsUIFileOperationCanceledReported docs_ui_file_op_canceled = 104 [(module) = "docsui"];
+ DocsUIFileOperationCopyMoveModeReported docs_ui_file_op_copy_move_mode_reported =
+ 105 [(module) = "docsui"];
+ DocsUIFileOperationFailureReported docs_ui_file_op_failure = 106 [(module) = "docsui"];
+ DocsUIFileOperationReported docs_ui_provider_file_op = 107 [(module) = "docsui"];
+ DocsUIInvalidScopedAccessRequestReported docs_ui_invalid_scoped_access_request =
+ 108 [(module) = "docsui"];
+ DocsUILaunchReported docs_ui_launch_reported = 109 [(module) = "docsui"];
+ DocsUIRootVisitedReported docs_ui_root_visited = 110 [(module) = "docsui"];
+ DocsUIStartupMsReported docs_ui_startup_ms = 111 [(module) = "docsui"];
+ DocsUIUserActionReported docs_ui_user_action_reported = 112 [(module) = "docsui"];
+ WifiEnabledStateChanged wifi_enabled_state_changed = 113 [(module) = "framework"];
+ WifiRunningStateChanged wifi_running_state_changed = 114
+ [(module) = "framework", deprecated = true];
+ AppCompacted app_compacted = 115 [(module) = "framework"];
+ NetworkDnsEventReported network_dns_event_reported = 116 [(module) = "resolv"];
DocsUIPickerLaunchedFromReported docs_ui_picker_launched_from_reported =
- 117 [(log_from_module) = "docsui"];
- DocsUIPickResultReported docs_ui_pick_result_reported =
- 118 [(log_from_module) = "docsui"];
- DocsUISearchModeReported docs_ui_search_mode_reported =
- 119 [(log_from_module) = "docsui"];
- DocsUISearchTypeReported docs_ui_search_type_reported =
- 120 [(log_from_module) = "docsui"];
- DataStallEvent data_stall_event = 121 [(log_from_module) = "network_stack"];
- RescuePartyResetReported rescue_party_reset_reported = 122;
- SignedConfigReported signed_config_reported = 123;
- GnssNiEventReported gnss_ni_event_reported = 124;
+ 117 [(module) = "docsui"];
+ DocsUIPickResultReported docs_ui_pick_result_reported = 118 [(module) = "docsui"];
+ DocsUISearchModeReported docs_ui_search_mode_reported = 119 [(module) = "docsui"];
+ DocsUISearchTypeReported docs_ui_search_type_reported = 120 [(module) = "docsui"];
+ DataStallEvent data_stall_event = 121 [(module) = "network_stack"];
+ RescuePartyResetReported rescue_party_reset_reported = 122 [(module) = "framework"];
+ SignedConfigReported signed_config_reported = 123 [(module) = "framework"];
+ GnssNiEventReported gnss_ni_event_reported = 124 [(module) = "framework"];
BluetoothLinkLayerConnectionEvent bluetooth_link_layer_connection_event =
- 125 [(log_from_module) = "bluetooth"];
+ 125 [(module) = "bluetooth"];
BluetoothAclConnectionStateChanged bluetooth_acl_connection_state_changed =
- 126 [(log_from_module) = "bluetooth"];
+ 126 [(module) = "bluetooth"];
BluetoothScoConnectionStateChanged bluetooth_sco_connection_state_changed =
- 127 [(log_from_module) = "bluetooth"];
- AppDowngraded app_downgraded = 128;
+ 127 [(module) = "bluetooth"];
+ AppDowngraded app_downgraded = 128 [(module) = "framework"];
AppOptimizedAfterDowngraded app_optimized_after_downgraded = 129;
- LowStorageStateChanged low_storage_state_changed = 130;
- GnssNfwNotificationReported gnss_nfw_notification_reported = 131;
- GnssConfigurationReported gnss_configuration_reported = 132;
+ LowStorageStateChanged low_storage_state_changed = 130 [(module) = "framework"];
+ GnssNfwNotificationReported gnss_nfw_notification_reported = 131 [(module) = "framework"];
+ GnssConfigurationReported gnss_configuration_reported = 132 [(module) = "framework"];
UsbPortOverheatEvent usb_port_overheat_event_reported = 133;
- NfcErrorOccurred nfc_error_occurred = 134;
- NfcStateChanged nfc_state_changed = 135;
- NfcBeamOccurred nfc_beam_occurred = 136;
- NfcCardemulationOccurred nfc_cardemulation_occurred = 137;
- NfcTagOccurred nfc_tag_occurred = 138;
- NfcHceTransactionOccurred nfc_hce_transaction_occurred = 139;
- SeStateChanged se_state_changed = 140;
- SeOmapiReported se_omapi_reported = 141;
- BroadcastDispatchLatencyReported broadcast_dispatch_latency_reported = 142;
- AttentionManagerServiceResultReported attention_manager_service_result_reported = 143;
- AdbConnectionChanged adb_connection_changed = 144;
+ NfcErrorOccurred nfc_error_occurred = 134 [(module) = "nfc"];
+ NfcStateChanged nfc_state_changed = 135 [(module) = "nfc"];
+ NfcBeamOccurred nfc_beam_occurred = 136 [(module) = "nfc"];
+ NfcCardemulationOccurred nfc_cardemulation_occurred = 137 [(module) = "nfc"];
+ NfcTagOccurred nfc_tag_occurred = 138 [(module) = "nfc"];
+ NfcHceTransactionOccurred nfc_hce_transaction_occurred = 139 [(module) = "nfc"];
+ SeStateChanged se_state_changed = 140 [(module) = "secure_element"];
+ SeOmapiReported se_omapi_reported = 141 [(module) = "secure_element"];
+ BroadcastDispatchLatencyReported broadcast_dispatch_latency_reported =
+ 142 [(module) = "framework"];
+ AttentionManagerServiceResultReported attention_manager_service_result_reported =
+ 143 [(module) = "framework"];
+ AdbConnectionChanged adb_connection_changed = 144 [(module) = "framework"];
SpeechDspStatReported speech_dsp_stat_reported = 145;
- UsbContaminantReported usb_contaminant_reported = 146;
- WatchdogRollbackOccurred watchdog_rollback_occurred = 147;
- BiometricSystemHealthIssueDetected biometric_system_health_issue_detected = 148;
- BubbleUIChanged bubble_ui_changed = 149;
- ScheduledJobConstraintChanged scheduled_job_constraint_changed = 150;
+ UsbContaminantReported usb_contaminant_reported = 146 [(module) = "framework"];
+ WatchdogRollbackOccurred watchdog_rollback_occurred =
+ 147 [(module) = "framework", (module) = "statsd"];
+ BiometricSystemHealthIssueDetected biometric_system_health_issue_detected =
+ 148 [(module) = "framework"];
+ BubbleUIChanged bubble_ui_changed = 149 [(module) = "sysui"];
+ ScheduledJobConstraintChanged scheduled_job_constraint_changed =
+ 150 [(module) = "framework"];
BluetoothActiveDeviceChanged bluetooth_active_device_changed =
- 151 [(log_from_module) = "bluetooth"];
+ 151 [(module) = "bluetooth"];
BluetoothA2dpPlaybackStateChanged bluetooth_a2dp_playback_state_changed =
- 152 [(log_from_module) = "bluetooth"];
+ 152 [(module) = "bluetooth"];
BluetoothA2dpCodecConfigChanged bluetooth_a2dp_codec_config_changed =
- 153 [(log_from_module) = "bluetooth"];
+ 153 [(module) = "bluetooth"];
BluetoothA2dpCodecCapabilityChanged bluetooth_a2dp_codec_capability_changed =
- 154 [(log_from_module) = "bluetooth"];
+ 154 [(module) = "bluetooth"];
BluetoothA2dpAudioUnderrunReported bluetooth_a2dp_audio_underrun_reported =
- 155 [(log_from_module) = "bluetooth"];
+ 155 [(module) = "bluetooth"];
BluetoothA2dpAudioOverrunReported bluetooth_a2dp_audio_overrun_reported =
- 156 [(log_from_module) = "bluetooth"];
+ 156 [(module) = "bluetooth"];
BluetoothDeviceRssiReported bluetooth_device_rssi_reported =
- 157 [(log_from_module) = "bluetooth"];
+ 157 [(module) = "bluetooth"];
BluetoothDeviceFailedContactCounterReported
- bluetooth_device_failed_contact_counter_reported = 158 [(log_from_module) = "bluetooth"];
+ bluetooth_device_failed_contact_counter_reported = 158 [(module) = "bluetooth"];
BluetoothDeviceTxPowerLevelReported bluetooth_device_tx_power_level_reported =
- 159 [(log_from_module) = "bluetooth"];
+ 159 [(module) = "bluetooth"];
BluetoothHciTimeoutReported bluetooth_hci_timeout_reported =
- 160 [(log_from_module) = "bluetooth"];
+ 160 [(module) = "bluetooth"];
BluetoothQualityReportReported bluetooth_quality_report_reported =
- 161 [(log_from_module) = "bluetooth"];
+ 161 [(module) = "bluetooth"];
BluetoothDeviceInfoReported bluetooth_device_info_reported =
- 162 [(log_from_module) = "bluetooth"];
+ 162 [(module) = "bluetooth"];
BluetoothRemoteVersionInfoReported bluetooth_remote_version_info_reported =
- 163 [(log_from_module) = "bluetooth"];
+ 163 [(module) = "bluetooth"];
BluetoothSdpAttributeReported bluetooth_sdp_attribute_reported =
- 164 [(log_from_module) = "bluetooth"];
+ 164 [(module) = "bluetooth"];
BluetoothBondStateChanged bluetooth_bond_state_changed =
- 165 [(log_from_module) = "bluetooth"];
+ 165 [(module) = "bluetooth"];
BluetoothClassicPairingEventReported bluetooth_classic_pairing_event_reported =
- 166 [(log_from_module) = "bluetooth"];
+ 166 [(module) = "bluetooth"];
BluetoothSmpPairingEventReported bluetooth_smp_pairing_event_reported =
- 167 [(log_from_module) = "bluetooth"];
- ScreenTimeoutExtensionReported screen_timeout_extension_reported = 168;
- ProcessStartTime process_start_time = 169;
+ 167 [(module) = "bluetooth"];
+ ScreenTimeoutExtensionReported screen_timeout_extension_reported =
+ 168 [(module) = "framework"];
+ ProcessStartTime process_start_time = 169 [(module) = "framework"];
PermissionGrantRequestResultReported permission_grant_request_result_reported =
- 170 [(log_from_module) = "permissioncontroller"];
+ 170 [(module) = "permissioncontroller"];
BluetoothSocketConnectionStateChanged bluetooth_socket_connection_state_changed = 171;
- DeviceIdentifierAccessDenied device_identifier_access_denied = 172;
- BubbleDeveloperErrorReported bubble_developer_error_reported = 173;
- AssistGestureStageReported assist_gesture_stage_reported = 174;
- AssistGestureFeedbackReported assist_gesture_feedback_reported = 175;
- AssistGestureProgressReported assist_gesture_progress_reported = 176;
- TouchGestureClassified touch_gesture_classified = 177;
- HiddenApiUsed hidden_api_used = 178 [(allow_from_any_uid) = true];
- StyleUIChanged style_ui_changed = 179 [(log_from_module) = "style"];
+ DeviceIdentifierAccessDenied device_identifier_access_denied =
+ 172 [(module) = "telephony_common"];
+ BubbleDeveloperErrorReported bubble_developer_error_reported = 173 [(module) = "framework"];
+ AssistGestureStageReported assist_gesture_stage_reported = 174 [(module) = "sysui"];
+ AssistGestureFeedbackReported assist_gesture_feedback_reported = 175 [(module) = "sysui"];
+ AssistGestureProgressReported assist_gesture_progress_reported = 176 [(module) = "sysui"];
+ TouchGestureClassified touch_gesture_classified = 177 [(module) = "framework"];
+ HiddenApiUsed hidden_api_used = 178 [(module) = "framework"];
+ StyleUIChanged style_ui_changed = 179 [(module) = "sysui"];
PrivacyIndicatorsInteracted privacy_indicators_interacted =
- 180 [(log_from_module) = "permissioncontroller"];
- AppInstallOnExternalStorageReported app_install_on_external_storage_reported = 181;
- NetworkStackReported network_stack_reported = 182 [(log_from_module) = "network_stack"];
- AppMovedStorageReported app_moved_storage_reported = 183;
- BiometricEnrolled biometric_enrolled = 184;
- SystemServerWatchdogOccurred system_server_watchdog_occurred = 185;
- TombStoneOccurred tomb_stone_occurred = 186;
+ 180 [(module) = "permissioncontroller"];
+ AppInstallOnExternalStorageReported app_install_on_external_storage_reported =
+ 181 [(module) = "framework"];
+ NetworkStackReported network_stack_reported = 182 [(module) = "network_stack"];
+ AppMovedStorageReported app_moved_storage_reported = 183 [(module) = "framework"];
+ BiometricEnrolled biometric_enrolled = 184 [(module) = "framework"];
+ SystemServerWatchdogOccurred system_server_watchdog_occurred = 185 [(module) = "framework"];
+ TombStoneOccurred tomb_stone_occurred = 186 [(module) = "framework"];
BluetoothClassOfDeviceReported bluetooth_class_of_device_reported =
- 187 [(log_from_module) = "bluetooth"];
+ 187 [(module) = "bluetooth"];
IntelligenceEventReported intelligence_event_reported =
- 188 [(log_from_module) = "intelligence"];
- ThermalThrottlingSeverityStateChanged thermal_throttling_severity_state_changed = 189;
+ 188 [(module) = "intelligence"];
+ ThermalThrottlingSeverityStateChanged thermal_throttling_severity_state_changed =
+ 189 [(module) = "framework"];
RoleRequestResultReported role_request_result_reported =
- 190 [(log_from_module) = "permissioncontroller"];
+ 190 [(module) = "permissioncontroller"];
MediametricsAudiopolicyReported mediametrics_audiopolicy_reported = 191;
MediametricsAudiorecordReported mediametrics_audiorecord_reported = 192;
MediametricsAudiothreadReported mediametrics_audiothread_reported = 193;
@@ -321,131 +344,258 @@ message Atom {
MediametricsMediadrmReported mediametrics_mediadrm_reported = 198;
MediametricsNuPlayerReported mediametrics_nuplayer_reported = 199;
MediametricsRecorderReported mediametrics_recorder_reported = 200;
- CarPowerStateChanged car_power_state_changed = 203;
- GarageModeInfo garage_mode_info = 204;
- TestAtomReported test_atom_reported = 205 [(log_from_module) = "cts"];
- ContentCaptureCallerMismatchReported content_capture_caller_mismatch_reported = 206;
- ContentCaptureServiceEvents content_capture_service_events = 207;
- ContentCaptureSessionEvents content_capture_session_events = 208;
- ContentCaptureFlushed content_capture_flushed = 209;
- LocationManagerApiUsageReported location_manager_api_usage_reported = 210;
+ MediametricsDrmManagerReported mediametrics_drmmanager_reported = 201;
+ CarPowerStateChanged car_power_state_changed = 203 [(module) = "car"];
+ GarageModeInfo garage_mode_info = 204 [(module) = "car"];
+ TestAtomReported test_atom_reported = 205 [(module) = "cts"];
+ ContentCaptureCallerMismatchReported content_capture_caller_mismatch_reported =
+ 206 [(module) = "framework"];
+ ContentCaptureServiceEvents content_capture_service_events = 207 [(module) = "framework"];
+ ContentCaptureSessionEvents content_capture_session_events = 208 [(module) = "framework"];
+ ContentCaptureFlushed content_capture_flushed = 209 [(module) = "framework"];
+ LocationManagerApiUsageReported location_manager_api_usage_reported =
+ 210 [(module) = "framework"];
ReviewPermissionsFragmentResultReported review_permissions_fragment_result_reported =
- 211 [(log_from_module) = "permissioncontroller"];
+ 211 [(module) = "permissioncontroller"];
RuntimePermissionsUpgradeResult runtime_permissions_upgrade_result =
- 212 [(log_from_module) = "permissioncontroller"];
+ 212 [(module) = "permissioncontroller"];
GrantPermissionsActivityButtonActions grant_permissions_activity_button_actions =
- 213 [(log_from_module) = "permissioncontroller"];
+ 213 [(module) = "permissioncontroller"];
LocationAccessCheckNotificationAction location_access_check_notification_action =
- 214 [(log_from_module) = "permissioncontroller"];
+ 214 [(module) = "permissioncontroller"];
AppPermissionFragmentActionReported app_permission_fragment_action_reported =
- 215 [(log_from_module) = "permissioncontroller"];
+ 215 [(module) = "permissioncontroller"];
AppPermissionFragmentViewed app_permission_fragment_viewed =
- 216 [(log_from_module) = "permissioncontroller"];
+ 216 [(module) = "permissioncontroller"];
AppPermissionsFragmentViewed app_permissions_fragment_viewed =
- 217 [(log_from_module) = "permissioncontroller"];
+ 217 [(module) = "permissioncontroller"];
PermissionAppsFragmentViewed permission_apps_fragment_viewed =
- 218 [(log_from_module) = "permissioncontroller"];
- ExclusionRectStateChanged exclusion_rect_state_changed = 223;
- BackGesture back_gesture_reported_reported = 224;
-
+ 218 [(module) = "permissioncontroller"];
+ TextSelectionEvent text_selection_event = 219 [(module) = "textclassifier"];
+ TextLinkifyEvent text_linkify_event = 220 [(module) = "textclassifier"];
+ ConversationActionsEvent conversation_actions_event = 221 [(module) = "textclassifier"];
+ LanguageDetectionEvent language_detection_event = 222 [(module) = "textclassifier"];
+ ExclusionRectStateChanged exclusion_rect_state_changed = 223 [(module) = "framework"];
+ BackGesture back_gesture_reported_reported = 224 [(module) = "sysui"];
UpdateEngineUpdateAttemptReported update_engine_update_attempt_reported = 225;
UpdateEngineSuccessfulUpdateReported update_engine_successful_update_reported = 226;
+ CameraActionEvent camera_action_event = 227 [(module) = "framework"];
AppCompatibilityChangeReported app_compatibility_change_reported =
- 228 [(allow_from_any_uid) = true];
- PerfettoUploaded perfetto_uploaded =
- 229 [(log_from_module) = "perfetto"];
- VmsClientConnectionStateChanged vms_client_connection_state_changed = 230;
- BootTimeEventDuration boot_time_event_duration_reported = 239;
- BootTimeEventElapsedTime boot_time_event_elapsed_time_reported = 240;
+ 228 [(module) = "framework"];
+ PerfettoUploaded perfetto_uploaded = 229 [(module) = "perfetto"];
+ VmsClientConnectionStateChanged vms_client_connection_state_changed =
+ 230 [(module) = "car"];
+ MediaProviderScanOccurred media_provider_scan_occurred = 233 [(module) = "mediaprovider"];
+ MediaContentDeleted media_content_deleted = 234 [(module) = "mediaprovider"];
+ MediaProviderPermissionRequested media_provider_permission_requested =
+ 235 [(module) = "mediaprovider"];
+ MediaProviderSchemaChanged media_provider_schema_changed = 236 [(module) = "mediaprovider"];
+ MediaProviderIdleMaintenanceFinished media_provider_idle_maintenance_finished =
+ 237 [(module) = "mediaprovider"];
+ RebootEscrowRecoveryReported reboot_escrow_recovery_reported = 238 [(module) = "framework"];
+ BootTimeEventDuration boot_time_event_duration_reported = 239 [(module) = "framework"];
+ BootTimeEventElapsedTime boot_time_event_elapsed_time_reported =
+ 240 [(module) = "framework"];
BootTimeEventUtcTime boot_time_event_utc_time_reported = 241;
- BootTimeEventErrorCode boot_time_event_error_code_reported = 242;
- UserspaceRebootReported userspace_reboot_reported = 243 [(log_from_module) = "framework"];
+ BootTimeEventErrorCode boot_time_event_error_code_reported = 242 [(module) = "framework"];
+ UserspaceRebootReported userspace_reboot_reported = 243 [(module) = "framework"];
+ NotificationReported notification_reported = 244 [(module) = "framework"];
+ NotificationPanelReported notification_panel_reported = 245 [(module) = "sysui"];
+ NotificationChannelModified notification_channel_modified = 246 [(module) = "framework"];
+ IntegrityCheckResultReported integrity_check_result_reported = 247 [(module) = "framework"];
+ IntegrityRulesPushed integrity_rules_pushed = 248 [(module) = "framework"];
+ CellBroadcastMessageReported cb_message_reported =
+ 249 [(module) = "cellbroadcast"];
+ CellBroadcastMessageError cb_message_error =
+ 250 [(module) = "cellbroadcast"];
+ WifiHealthStatReported wifi_health_stat_reported = 251 [(module) = "wifi"];
+ WifiFailureStatReported wifi_failure_stat_reported = 252 [(module) = "wifi"];
+ WifiConnectionResultReported wifi_connection_result_reported = 253 [(module) = "wifi"];
+ AppFreezeChanged app_freeze_changed = 254 [(module) = "framework"];
SnapshotMergeReported snapshot_merge_reported = 255;
- NetworkIpProvisioningReported network_ip_provisioning_reported = 290 [(log_from_module) = "network_stack"];
- NetworkDhcpRenewReported network_dhcp_renew_reported = 291 [(log_from_module) = "network_stack"];
- NetworkValidationReported network_validation_reported = 292 [(log_from_module) = "network_stack"];
- NetworkStackQuirkReported network_stack_quirk_reported = 293 [(log_from_module) = "network_stack"];
+ ForegroundServiceAppOpSessionEnded foreground_service_app_op_session_ended =
+ 256 [(module) = "framework"];
+ DisplayJankReported display_jank_reported = 257;
+ AppStandbyBucketChanged app_standby_bucket_changed = 258 [(module) = "framework"];
+ SharesheetStarted sharesheet_started = 259 [(module) = "framework"];
+ RankingSelected ranking_selected = 260 [(module) = "framework", (module) = "sysui"];
+ TvSettingsUIInteracted tvsettings_ui_interacted = 261 [(module) = "tv_settings"];
+ LauncherStaticLayout launcher_snapshot = 262 [(module) = "sysui"];
+ PackageInstallerV2Reported package_installer_v2_reported = 263 [(module) = "framework"];
+ UserLifecycleJourneyReported user_lifecycle_journey_reported = 264 [(module) = "framework"];
+ UserLifecycleEventOccurred user_lifecycle_event_occurred = 265 [(module) = "framework"];
+ AccessibilityShortcutReported accessibility_shortcut_reported =
+ 266 [(module) = "framework"];
+ AccessibilityServiceReported accessibility_service_reported = 267 [(module) = "settings"];
+ DocsUIDragAndDropReported docs_ui_drag_and_drop_reported = 268 [(module) = "docsui"];
+ AppUsageEventOccurred app_usage_event_occurred = 269 [(module) = "framework"];
+ AutoRevokeNotificationClicked auto_revoke_notification_clicked =
+ 270 [(module) = "permissioncontroller"];
+ AutoRevokeFragmentAppViewed auto_revoke_fragment_app_viewed =
+ 271 [(module) = "permissioncontroller"];
+ AutoRevokedAppInteraction auto_revoked_app_interaction =
+ 272 [(module) = "permissioncontroller", (module) = "settings"];
+ AppPermissionGroupsFragmentAutoRevokeAction
+ app_permission_groups_fragment_auto_revoke_action =
+ 273 [(module) = "permissioncontroller"];
+ EvsUsageStatsReported evs_usage_stats_reported = 274 [(module) = "evs"];
+ AudioPowerUsageDataReported audio_power_usage_data_reported = 275;
+ TvTunerStateChanged tv_tuner_state_changed = 276 [(module) = "framework"];
+ MediaOutputOpSwitchReported mediaoutput_op_switch_reported =
+ 277 [(module) = "settings"];
+ CellBroadcastMessageFiltered cb_message_filtered =
+ 278 [(module) = "cellbroadcast"];
+ TvTunerDvrStatus tv_tuner_dvr_status = 279 [(module) = "framework"];
+ TvCasSessionOpenStatus tv_cas_session_open_status =
+ 280 [(module) = "framework"];
+ AssistantInvocationReported assistant_invocation_reported = 281 [(module) = "framework"];
+ DisplayWakeReported display_wake_reported = 282 [(module) = "framework"];
+ CarUserHalModifyUserRequestReported car_user_hal_modify_user_request_reported =
+ 283 [(module) = "car"];
+ CarUserHalModifyUserResponseReported car_user_hal_modify_user_response_reported =
+ 284 [(module) = "car"];
+ CarUserHalPostSwitchResponseReported car_user_hal_post_switch_response_reported =
+ 285 [(module) = "car"];
+ CarUserHalInitialUserInfoRequestReported car_user_hal_initial_user_info_request_reported =
+ 286 [(module) = "car"];
+ CarUserHalInitialUserInfoResponseReported car_user_hal_initial_user_info_response_reported =
+ 287 [(module) = "car"];
+ CarUserHalUserAssociationRequestReported car_user_hal_user_association_request_reported =
+ 288 [(module) = "car"];
+ CarUserHalSetUserAssociationResponseReported car_user_hal_set_user_association_response_reported =
+ 289 [(module) = "car"];
+ NetworkIpProvisioningReported network_ip_provisioning_reported =
+ 290 [(module) = "network_stack"];
+ NetworkDhcpRenewReported network_dhcp_renew_reported = 291 [(module) = "network_stack"];
+ NetworkValidationReported network_validation_reported = 292 [(module) = "network_stack"];
+ NetworkStackQuirkReported network_stack_quirk_reported = 293 [(module) = "network_stack"];
+ MediametricsAudioRecordDeviceUsageReported mediametrics_audiorecorddeviceusage_reported =
+ 294;
+ MediametricsAudioThreadDeviceUsageReported mediametrics_audiothreaddeviceusage_reported =
+ 295;
+ MediametricsAudioTrackDeviceUsageReported mediametrics_audiotrackdeviceusage_reported =
+ 296;
+ MediametricsAudioDeviceConnectionReported mediametrics_audiodeviceconnection_reported =
+ 297;
+ BlobCommitted blob_committed = 298 [(module) = "framework"];
+ BlobLeased blob_leased = 299 [(module) = "framework"];
+ BlobOpened blob_opened = 300 [(module) = "framework"];
+ ContactsProviderStatusReported contacts_provider_status_reported = 301;
KeystoreKeyEventReported keystore_key_event_reported = 302;
- NetworkTetheringReported network_tethering_reported = 303 [(log_from_module) = "network_tethering"];
+ NetworkTetheringReported network_tethering_reported =
+ 303 [(module) = "network_tethering"];
+ ImeTouchReported ime_touch_reported = 304 [(module) = "sysui"];
+
+ // StatsdStats tracks platform atoms with ids upto 500.
+ // Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value.
}
// Pulled events will start at field 10000.
- // Next: 10080
+ // Next: 10084
oneof pulled {
- WifiBytesTransfer wifi_bytes_transfer = 10000;
- WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
- MobileBytesTransfer mobile_bytes_transfer = 10002;
- MobileBytesTransferByFgBg mobile_bytes_transfer_by_fg_bg = 10003;
- BluetoothBytesTransfer bluetooth_bytes_transfer = 10006;
- KernelWakelock kernel_wakelock = 10004;
- SubsystemSleepState subsystem_sleep_state = 10005;
- CpuTimePerFreq cpu_time_per_freq = 10008;
- CpuTimePerUid cpu_time_per_uid = 10009;
- CpuTimePerUidFreq cpu_time_per_uid_freq = 10010;
- WifiActivityInfo wifi_activity_info = 10011;
- ModemActivityInfo modem_activity_info = 10012;
- BluetoothActivityInfo bluetooth_activity_info = 10007;
- ProcessMemoryState process_memory_state = 10013;
- SystemElapsedRealtime system_elapsed_realtime = 10014;
- SystemUptime system_uptime = 10015;
- CpuActiveTime cpu_active_time = 10016;
- CpuClusterTime cpu_cluster_time = 10017;
- DiskSpace disk_space = 10018 [deprecated=true];
- RemainingBatteryCapacity remaining_battery_capacity = 10019;
- FullBatteryCapacity full_battery_capacity = 10020;
- Temperature temperature = 10021;
- BinderCalls binder_calls = 10022;
- BinderCallsExceptions binder_calls_exceptions = 10023;
- LooperStats looper_stats = 10024;
- DiskStats disk_stats = 10025;
- DirectoryUsage directory_usage = 10026;
- AppSize app_size = 10027;
- CategorySize category_size = 10028;
- ProcStats proc_stats = 10029;
- BatteryVoltage battery_voltage = 10030;
- NumFingerprintsEnrolled num_fingerprints_enrolled = 10031;
- DiskIo disk_io = 10032;
- PowerProfile power_profile = 10033;
- ProcStatsPkgProc proc_stats_pkg_proc = 10034;
- ProcessCpuTime process_cpu_time = 10035;
- NativeProcessMemoryState native_process_memory_state = 10036;
- CpuTimePerThreadFreq cpu_time_per_thread_freq = 10037;
+ WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"];
+ WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"];
+ MobileBytesTransfer mobile_bytes_transfer =
+ 10002 [(module) = "framework", (truncate_timestamp) = true];
+ MobileBytesTransferByFgBg mobile_bytes_transfer_by_fg_bg =
+ 10003 [(module) = "framework", (truncate_timestamp) = true];
+ BluetoothBytesTransfer bluetooth_bytes_transfer = 10006 [(module) = "framework"];
+ KernelWakelock kernel_wakelock = 10004 [(module) = "framework"];
+ SubsystemSleepState subsystem_sleep_state = 10005 [(module) = "statsdtest"];
+ CpuTimePerFreq cpu_time_per_freq = 10008 [(module) = "framework"];
+ CpuTimePerUid cpu_time_per_uid = 10009 [(module) = "framework", (module) = "statsdtest"];
+ CpuTimePerUidFreq cpu_time_per_uid_freq =
+ 10010 [(module) = "framework", (module) = "statsd"];
+ WifiActivityInfo wifi_activity_info = 10011 [(module) = "framework"];
+ ModemActivityInfo modem_activity_info = 10012 [(module) = "framework"];
+ BluetoothActivityInfo bluetooth_activity_info = 10007 [(module) = "framework"];
+ ProcessMemoryState process_memory_state = 10013 [(module) = "framework"];
+ SystemElapsedRealtime system_elapsed_realtime = 10014 [(module) = "framework"];
+ SystemUptime system_uptime = 10015 [(module) = "framework"];
+ CpuActiveTime cpu_active_time = 10016 [(module) = "framework", (module) = "statsdtest"];
+ CpuClusterTime cpu_cluster_time = 10017 [(module) = "framework"];
+ DiskSpace disk_space = 10018 [deprecated=true, (module) = "statsdtest"];
+ RemainingBatteryCapacity remaining_battery_capacity = 10019 [(module) = "framework"];
+ FullBatteryCapacity full_battery_capacity = 10020 [(module) = "framework"];
+ Temperature temperature = 10021 [(module) = "framework", (module) = "statsdtest"];
+ BinderCalls binder_calls = 10022 [(module) = "framework", (module) = "statsd"];
+ BinderCallsExceptions binder_calls_exceptions = 10023 [(module) = "framework"];
+ LooperStats looper_stats = 10024 [(module) = "framework", (module) = "statsd"];
+ DiskStats disk_stats = 10025 [(module) = "framework"];
+ DirectoryUsage directory_usage = 10026 [(module) = "framework"];
+ AppSize app_size = 10027 [(module) = "framework"];
+ CategorySize category_size = 10028 [(module) = "framework"];
+ ProcStats proc_stats = 10029 [(module) = "framework"];
+ BatteryVoltage battery_voltage = 10030 [(module) = "framework"];
+ NumFingerprintsEnrolled num_fingerprints_enrolled = 10031 [(module) = "framework"];
+ DiskIo disk_io = 10032 [(module) = "framework"];
+ PowerProfile power_profile = 10033 [(module) = "framework"];
+ ProcStatsPkgProc proc_stats_pkg_proc = 10034 [(module) = "framework"];
+ ProcessCpuTime process_cpu_time = 10035 [(module) = "framework"];
+ CpuTimePerThreadFreq cpu_time_per_thread_freq = 10037 [(module) = "framework"];
OnDevicePowerMeasurement on_device_power_measurement = 10038;
- DeviceCalculatedPowerUse device_calculated_power_use = 10039;
- DeviceCalculatedPowerBlameUid device_calculated_power_blame_uid = 10040;
- DeviceCalculatedPowerBlameOther device_calculated_power_blame_other = 10041;
- ProcessMemoryHighWaterMark process_memory_high_water_mark = 10042;
- BatteryLevel battery_level = 10043;
- BuildInformation build_information = 10044;
- BatteryCycleCount battery_cycle_count = 10045;
- DebugElapsedClock debug_elapsed_clock = 10046;
- DebugFailingElapsedClock debug_failing_elapsed_clock = 10047;
- NumFacesEnrolled num_faces_enrolled = 10048;
- RoleHolder role_holder = 10049;
- DangerousPermissionState dangerous_permission_state = 10050;
- TrainInfo train_info = 10051;
- TimeZoneDataInfo time_zone_data_info = 10052;
- ExternalStorageInfo external_storage_info = 10053;
+ DeviceCalculatedPowerUse device_calculated_power_use = 10039 [(module) = "framework"];
+ DeviceCalculatedPowerBlameUid device_calculated_power_blame_uid =
+ 10040 [(module) = "framework"];
+ DeviceCalculatedPowerBlameOther device_calculated_power_blame_other =
+ 10041 [(module) = "framework"];
+ ProcessMemoryHighWaterMark process_memory_high_water_mark = 10042 [(module) = "framework"];
+ BatteryLevel battery_level = 10043 [(module) = "framework"];
+ BuildInformation build_information = 10044 [(module) = "framework"];
+ BatteryCycleCount battery_cycle_count = 10045 [(module) = "framework"];
+ DebugElapsedClock debug_elapsed_clock = 10046 [(module) = "framework"];
+ DebugFailingElapsedClock debug_failing_elapsed_clock = 10047 [(module) = "framework"];
+ NumFacesEnrolled num_faces_enrolled = 10048 [(module) = "framework"];
+ RoleHolder role_holder = 10049 [(module) = "framework"];
+ DangerousPermissionState dangerous_permission_state = 10050 [(module) = "framework"];
+ TrainInfo train_info = 10051 [(module) = "statsd"];
+ TimeZoneDataInfo time_zone_data_info = 10052 [(module) = "framework"];
+ ExternalStorageInfo external_storage_info = 10053 [(module) = "framework"];
GpuStatsGlobalInfo gpu_stats_global_info = 10054;
GpuStatsAppInfo gpu_stats_app_info = 10055;
- SystemIonHeapSize system_ion_heap_size = 10056;
- AppsOnExternalStorageInfo apps_on_external_storage_info = 10057;
- FaceSettings face_settings = 10058;
- CoolingDevice cooling_device = 10059;
- AppOps app_ops = 10060;
- ProcessSystemIonHeapSize process_system_ion_heap_size = 10061;
- VmsClientStats vms_client_stats = 10065;
- NotificationRemoteViews notification_remote_views = 10066;
- VoiceCallSession voice_call_session = 10076 [(log_from_module) = "telephony"];
- VoiceCallRatUsage voice_call_rat_usage = 10077 [(log_from_module) = "telephony"];
- SimSlotState sim_slot_state = 10078 [(log_from_module) = "telephony"];
- SupportedRadioAccessFamily supported_radio_access_family =
- 10079 [(log_from_module) = "telephony"];
+ SystemIonHeapSize system_ion_heap_size = 10056 [deprecated = true, (module) = "framework"];
+ AppsOnExternalStorageInfo apps_on_external_storage_info = 10057 [(module) = "framework"];
+ FaceSettings face_settings = 10058 [(module) = "framework"];
+ CoolingDevice cooling_device = 10059 [(module) = "framework"];
+ AppOps app_ops = 10060 [(module) = "framework"];
+ ProcessSystemIonHeapSize process_system_ion_heap_size = 10061 [(module) = "framework"];
+ SurfaceflingerStatsGlobalInfo surfaceflinger_stats_global_info = 10062;
+ SurfaceflingerStatsLayerInfo surfaceflinger_stats_layer_info = 10063;
+ ProcessMemorySnapshot process_memory_snapshot = 10064 [(module) = "framework"];
+ VmsClientStats vms_client_stats = 10065 [(module) = "car"];
+ NotificationRemoteViews notification_remote_views = 10066 [(module) = "framework"];
+ DangerousPermissionStateSampled dangerous_permission_state_sampled =
+ 10067 [(module) = "framework"];
+ GraphicsStats graphics_stats = 10068;
+ RuntimeAppOpAccess runtime_app_op_access = 10069 [(module) = "framework"];
+ IonHeapSize ion_heap_size = 10070 [(module) = "framework"];
+ PackageNotificationPreferences package_notification_preferences =
+ 10071 [(module) = "framework"];
+ PackageNotificationChannelPreferences package_notification_channel_preferences =
+ 10072 [(module) = "framework"];
+ PackageNotificationChannelGroupPreferences package_notification_channel_group_preferences =
+ 10073 [(module) = "framework"];
+ GnssStats gnss_stats = 10074 [(module) = "framework"];
+ AttributedAppOps attributed_app_ops = 10075 [(module) = "framework"];
+ VoiceCallSession voice_call_session = 10076 [(module) = "telephony"];
+ VoiceCallRatUsage voice_call_rat_usage = 10077 [(module) = "telephony"];
+ SimSlotState sim_slot_state = 10078 [(module) = "telephony"];
+ SupportedRadioAccessFamily supported_radio_access_family = 10079 [(module) = "telephony"];
+ SettingSnapshot setting_snapshot = 10080 [(module) = "framework"];
+ BlobInfo blob_info = 10081 [(module) = "framework"];
+ DataUsageBytesTransfer data_usage_bytes_transfer = 10082 [(module) = "framework"];
+ BytesTransferByTagAndMetered bytes_transfer_by_tag_and_metered =
+ 10083 [(module) = "framework"];
+ DNDModeProto dnd_mode_rule = 10084 [(module) = "framework"];
+ GeneralExternalStorageAccessStats general_external_storage_access_stats =
+ 10085 [(module) = "mediaprovider"];
}
// DO NOT USE field numbers above 100,000 in AOSP.
// Field numbers 100,000 - 199,999 are reserved for non-AOSP (e.g. OEMs) to use.
// Field numbers 200,000 and above are reserved for future use; do not use them at all.
+
+ reserved 10036;
}
/**
@@ -534,7 +684,8 @@ message ThermalThrottlingStateChanged {
*/
message ScreenStateChanged {
// New screen state, from frameworks/base/core/proto/android/view/enums.proto.
- optional android.view.DisplayStateEnum state = 1 [(state_field_option).option = EXCLUSIVE];
+ optional android.view.DisplayStateEnum state = 1
+ [(state_field_option).exclusive_state = true, (state_field_option).nested = false];
}
/**
@@ -545,10 +696,11 @@ message ScreenStateChanged {
* frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
*/
message UidProcessStateChanged {
- optional int32 uid = 1 [(state_field_option).option = PRIMARY, (is_uid) = true];
+ optional int32 uid = 1 [(state_field_option).primary_field = true, (is_uid) = true];
// The state, from frameworks/base/core/proto/android/app/enums.proto.
- optional android.app.ProcessStateEnum state = 2 [(state_field_option).option = EXCLUSIVE];
+ optional android.app.ProcessStateEnum state = 2
+ [(state_field_option).exclusive_state = true, (state_field_option).nested = false];
}
/**
@@ -580,7 +732,8 @@ message ActivityManagerSleepStateChanged {
ASLEEP = 1;
AWAKE = 2;
}
- optional State state = 1 [(state_field_option).option = EXCLUSIVE];
+ optional State state = 1
+ [(state_field_option).exclusive_state = true, (state_field_option).nested = false];
}
/**
@@ -599,7 +752,7 @@ message MemoryFactorStateChanged {
CRITICAL = 4; // critical memory.
}
- optional State factor = 1 [(state_field_option).option = EXCLUSIVE];
+ optional State factor = 1 [(state_field_option).exclusive_state = true];
}
/**
@@ -632,6 +785,96 @@ message CachedKillReported {
}
/**
+ * Logs the change in wifi health.
+ *
+ * Logged from:
+ * frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiDataStall.java
+ */
+message WifiHealthStatReported {
+ enum Band {
+ UNKNOWN = 0;
+ // All of 2.4GHz band
+ BAND_2G = 1;
+ // Frequencies in the range of [5150, 5250) GHz
+ BAND_5G_LOW = 2;
+ // Frequencies in the range of [5250, 5725) GHz
+ BAND_5G_MIDDLE = 3;
+ // Frequencies in the range of [5725, 5850) GHz
+ BAND_5G_HIGH = 4;
+ // Frequencies in the range of [5925, 6425) GHz
+ BAND_6G_LOW = 5;
+ // Frequencies in the range of [6425, 6875) GHz
+ BAND_6G_MIDDLE = 6;
+ // Frequencies in the range of [6875, 7125) GHz
+ BAND_6G_HIGH = 7;
+ }
+ // duration this stat is obtained over in milliseconds
+ optional int32 duration_millis = 1;
+ // whether wifi is classified as sufficient for the user's data traffic, determined
+ // by whether the calculated throughput exceeds the average demand within |duration_millis|
+ optional bool is_sufficient = 2;
+ // whether cellular data is available
+ optional bool is_cell_data_available = 3;
+ // the Band bucket the connected network is on
+ optional Band band = 4;
+}
+
+/**
+ * Logged when wifi detects a significant change in connection failure rate.
+ *
+ * Logged from: frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiHealthMonitor.java
+ *
+ */
+message WifiFailureStatReported {
+ enum AbnormalityType {
+ UNKNOWN = 0;
+ SIGNIFICANT_INCREASE = 1;
+ SIGNIFICANT_DECREASE = 2;
+ SIMPLY_HIGH = 3;
+ }
+ enum FailureType {
+ FAILURE_UNKNOWN = 0;
+ FAILURE_CONNECTION = 1;
+ FAILURE_ASSOCIATION_REJECTION = 2;
+ FAILURE_ASSOCIATION_TIMEOUT = 3;
+ FAILURE_AUTHENTICATION = 4;
+ FAILURE_NON_LOCAL_DISCONNECTION = 5;
+ FAILURE_SHORT_CONNECTION_DUE_TO_NON_LOCAL_DISCONNECTION = 6;
+ }
+ // Reason for uploading this stat
+ optional AbnormalityType abnormality_type = 1;
+ // The particular type of failure
+ optional FailureType failure_type = 2;
+ // How many times we have encountered this combination of AbnormalityType and FailureType
+ optional int32 failure_count = 3;
+}
+
+/**
+ * Logs whether a wifi connection is successful and reasons for failure if it isn't.
+ *
+ * Logged from:
+ * frameworks/opt/net/wifi/service/java/com/android/server/wifi/ClientModeImpl.java
+ */
+message WifiConnectionResultReported {
+ enum FailureCode {
+ FAILURE_UNKNOWN = 0;
+ FAILURE_ASSOCIATION_TIMEOUT = 1;
+ FAILURE_ASSOCIATION_REJECTION = 2;
+ FAILURE_AUTHENTICATION_GENERAL = 3;
+ FAILURE_AUTHENTICATION_EAP = 4;
+ FAILURE_DHCP = 5;
+ FAILURE_NETWORK_DISCONNECTION = 6;
+ FAILURE_ROAM_TIMEOUT = 7;
+ }
+ // true represents a successful connection
+ optional bool connection_result = 1;
+ // reason for the connection failure
+ optional FailureCode failure_code = 2;
+ // scan rssi before the connection attempt
+ optional int32 rssi = 3;
+}
+
+/**
* Logs when memory stats of a process is reported.
*
* Logged from:
@@ -686,7 +929,8 @@ message ProcessLifeCycleStateChanged {
* packages/apps/Bluetooth/src/com/android/bluetooth/gatt/AppScanStats.java
*/
message BleScanStateChanged {
- repeated AttributionNode attribution_node = 1;
+ repeated AttributionNode attribution_node = 1
+ [(state_field_option).primary_field_first_uid = true];
enum State {
OFF = 0;
@@ -694,14 +938,19 @@ message BleScanStateChanged {
// RESET indicates all ble stopped. Used when it (re)starts (e.g. after it crashes).
RESET = 2;
}
- optional State state = 2;
+ optional State state = 2 [
+ (state_field_option).exclusive_state = true,
+ (state_field_option).default_state_value = 0 /* State.OFF */,
+ (state_field_option).trigger_state_reset_value = 2 /* State.RESET */,
+ (state_field_option).nested = true
+ ];
// Does the scan have a filter.
- optional bool is_filtered = 3;
+ optional bool is_filtered = 3 [(state_field_option).primary_field = true];
// Whether the scan is a CALLBACK_TYPE_FIRST_MATCH scan. Called 'background' scan internally.
- optional bool is_first_match = 4;
+ optional bool is_first_match = 4 [(state_field_option).primary_field = true];
// Whether the scan set to piggy-back off the results of other scans (SCAN_MODE_OPPORTUNISTIC).
- optional bool is_opportunistic = 5;
+ optional bool is_opportunistic = 5 [(state_field_option).primary_field = true];
}
/**
@@ -833,11 +1082,23 @@ message ScheduledJobStateChanged {
FREQUENT = 2;
RARE = 3;
NEVER = 4;
+ RESTRICTED = 5;
}
optional Bucket standby_bucket = 5 [default = UNKNOWN];
// The job id (as assigned by the app).
optional int32 job_id = 6;
+
+ // One flag for each of the API constraints defined by Jobscheduler. Does not include implcit
+ // constraints as they are always assumed to be set.
+ optional bool has_charging_constraint = 7;
+ optional bool has_battery_not_low_constraint = 8;
+ optional bool has_storage_not_low_constraint = 9;
+ optional bool has_timing_delay_constraint = 10;
+ optional bool has_deadline_constraint = 11;
+ optional bool has_idle_constraint = 12;
+ optional bool has_connectivity_constraint = 13;
+ optional bool has_content_trigger_constraint = 14;
}
/**
@@ -919,14 +1180,15 @@ message CameraStateChanged {
* TODO
*/
message WakelockStateChanged {
- repeated AttributionNode attribution_node = 1;
+ repeated AttributionNode attribution_node = 1
+ [(state_field_option).primary_field_first_uid = true];
// The type (level) of the wakelock; e.g. a partial wakelock or a full wakelock.
// From frameworks/base/core/proto/android/os/enums.proto.
- optional android.os.WakeLockLevelEnum type = 2;
+ optional android.os.WakeLockLevelEnum type = 2 [(state_field_option).primary_field = true];
// The wakelock tag (Called tag in the Java API, sometimes name elsewhere).
- optional string tag = 3;
+ optional string tag = 3 [(state_field_option).primary_field = true];
enum State {
RELEASE = 0;
@@ -934,7 +1196,11 @@ message WakelockStateChanged {
CHANGE_RELEASE = 2;
CHANGE_ACQUIRE = 3;
}
- optional State state = 4;
+ optional State state = 4 [
+ (state_field_option).exclusive_state = true,
+ (state_field_option).default_state_value = 0,
+ (state_field_option).nested = true
+ ];
}
/**
@@ -984,7 +1250,8 @@ message BatterySaverModeStateChanged {
OFF = 0;
ON = 1;
}
- optional State state = 1;
+ optional State state = 1
+ [(state_field_option).exclusive_state = true, (state_field_option).nested = false];
}
/**
@@ -994,7 +1261,8 @@ message BatterySaverModeStateChanged {
* frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message DeviceIdleModeStateChanged {
- optional android.server.DeviceIdleModeEnum state = 1;
+ optional android.server.DeviceIdleModeEnum state = 1
+ [(state_field_option).exclusive_state = true, (state_field_option).nested = false];
}
@@ -1005,7 +1273,8 @@ message DeviceIdleModeStateChanged {
* frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message DeviceIdlingModeStateChanged {
- optional android.server.DeviceIdleModeEnum state = 1;
+ optional android.server.DeviceIdleModeEnum state = 1
+ [(state_field_option).exclusive_state = true, (state_field_option).nested = false];
}
/**
@@ -1038,7 +1307,8 @@ message BatteryLevelChanged {
*/
message ChargingStateChanged {
// State of the battery, from frameworks/base/core/proto/android/os/enums.proto.
- optional android.os.BatteryStatusEnum state = 1;
+ optional android.os.BatteryStatusEnum state = 1
+ [(state_field_option).exclusive_state = true, (state_field_option).nested = false];
}
/**
@@ -1049,7 +1319,8 @@ message ChargingStateChanged {
*/
message PluggedStateChanged {
// Whether the device is plugged in, from frameworks/base/core/proto/android/os/enums.proto.
- optional android.os.BatteryPluggedStateEnum state = 1;
+ optional android.os.BatteryPluggedStateEnum state = 1
+ [(state_field_option).exclusive_state = true, (state_field_option).nested = false];
}
/**
@@ -1067,18 +1338,8 @@ message WakeupAlarmOccurred {
// Name of source package (for historical reasons, since BatteryStats tracked it).
optional string package_name = 3;
- // These enum values match the STANDBY_BUCKET_XXX constants defined in UsageStatsManager.java.
- enum Bucket {
- UNKNOWN = 0;
- EXEMPTED = 5;
- ACTIVE = 10;
- WORKING_SET = 20;
- FREQUENT = 30;
- RARE = 40;
- NEVER = 50;
- }
// The App Standby bucket of the app that scheduled the alarm at the time the alarm fired.
- optional Bucket app_standby_bucket = 4;
+ optional AppStandbyBucketChanged.Bucket app_standby_bucket = 4;
}
/**
@@ -1139,6 +1400,8 @@ message WifiEnabledStateChanged {
}
/**
+ * This atom is deprecated starting in R.
+ *
* Logs when an app causes Wifi to run. In this context, 'to run' means to use Wifi Client Mode.
* TODO: Include support for Hotspot, perhaps by using an extra field to denote 'mode'.
* Note that Wifi Scanning is monitored separately in WifiScanStateChanged.
@@ -1789,12 +2052,15 @@ message WatchdogRollbackOccurred {
REASON_EXPLICIT_HEALTH_CHECK = 2;
REASON_APP_CRASH = 3;
REASON_APP_NOT_RESPONDING = 4;
+ REASON_NATIVE_CRASH_DURING_BOOT = 5;
}
optional RollbackReasonType rollback_reason = 4;
// Set by RollbackPackageHealthObserver to be the package that is failing when a rollback
// is initiated. Empty if the package is unknown.
optional string failing_package_name = 5;
+
+ optional TrainExperimentIds experiment_ids = 6 [(log_mode) = MODE_BYTES];
}
/**
@@ -2518,8 +2784,9 @@ message UsbConnectorStateChanged {
STATE_DISCONNECTED = 0;
STATE_CONNECTED = 1;
}
- optional State state = 1;
- optional string id = 2;
+ optional State state = 1
+ [(state_field_option).exclusive_state = true, (state_field_option).nested = false];
+ optional string id = 2 [(state_field_option).primary_field = true];
// Last active session in ms.
// 0 when the port is in connected state.
optional int64 last_connect_duration_millis = 3;
@@ -2750,21 +3017,32 @@ message PhoneStateChanged {
message BackGesture {
enum BackType {
- DEFAULT_BACK_TYPE = 0;
- COMPLETED = 1;
- COMPLETED_REJECTED = 2; // successful because coming from rejected area
- INCOMPLETE_EXCLUDED = 3; // would have been successful but in the exclusion area
- INCOMPLETE = 4;
+ DEFAULT_BACK_TYPE = 0;
+ COMPLETED = 1;
+ COMPLETED_REJECTED = 2; // successful because coming from rejected area
+ INCOMPLETE_EXCLUDED = 3; // would have been successful but in the exclusion area
+ INCOMPLETE = 4; // Unsuccessful, for reasons other than below.
+ INCOMPLETE_FAR_FROM_EDGE = 5; // Unsuccessful, far from the edge.
+ INCOMPLETE_MULTI_TOUCH = 6; // Unsuccessful, multi touch.
+ INCOMPLETE_LONG_PRESS = 7; // Unsuccessful, long press.
+ INCOMPLETE_VERTICAL_MOVE = 8; // Unsuccessful, move vertically.
}
optional BackType type = 1;
- optional int32 y_coordinate = 2; // y coordinate for ACTION_DOWN event
+ optional int32 y_coordinate = 2 [deprecated = true]; // y coordinate for ACTION_DOWN event
+ optional int32 start_x = 4; // X coordinate for ACTION_DOWN event.
+ optional int32 start_y = 5; // Y coordinate for ACTION_DOWN event.
+ optional int32 end_x = 6; // X coordinate for ACTION_MOVE event.
+ optional int32 end_y = 7; // Y coordinate for ACTION_MOVE event.
+ optional int32 left_boundary = 8; // left edge width + left inset
+ optional int32 right_boundary = 9; // screen width - (right edge width + right inset)
+
enum WindowHorizontalLocation {
DEFAULT_LOCATION = 0;
LEFT = 1;
RIGHT = 2;
}
- optional WindowHorizontalLocation x_location = 3;
+ optional WindowHorizontalLocation x_location = 3 [deprecated = true];
}
message ExclusionRectStateChanged {
@@ -2783,14 +3061,125 @@ message ExclusionRectStateChanged {
optional int32 duration_millis = 7;
}
+/**
+ * Logs when IME is on.
+ *
+ * Logged from: /packages/SystemUI/src/com/android/systemui/
+ statusbar/phone/NavigationBarView.java
+ *
+ */
+message ImeTouchReported {
+ optional int32 x_coordinate = 1; // X coordinate for ACTION_DOWN event.
+ optional int32 y_coordinate = 2; // Y coordinate for ACTION_DOWN event.
+}
+
+/**
+ * Logs when Launcher (HomeScreen) UI has changed or was interacted.
+ *
+ * Logged from:
+ * packages/apps/Launcher3
+ */
message LauncherUIChanged {
- optional android.stats.launcher.LauncherAction action = 1;
+ optional android.stats.launcher.LauncherAction action = 1 [deprecated = true];
optional android.stats.launcher.LauncherState src_state = 2;
optional android.stats.launcher.LauncherState dst_state = 3;
- optional android.stats.launcher.LauncherExtension extension = 4 [(log_mode) = MODE_BYTES];
- optional bool is_swipe_up_enabled = 5;
+ optional android.stats.launcher.LauncherExtension extension = 4 [(log_mode) = MODE_BYTES, deprecated = true];
+ optional bool is_swipe_up_enabled = 5 [deprecated = true];
+
+ // The event id (e.g., app launch, drag and drop, long press)
+ optional int32 event_id = 6;
+ // The event's source or target id (e.g., icon, task, button)
+ optional int32 target_id = 7;
+ // If the target needs to be tracked, use this id field
+ optional int32 instance_id = 8;
+ optional int32 uid = 9 [(is_uid) = true];
+ optional string package_name = 10;
+ optional string component_name = 11;
+
+ // (x, y) coordinate and the index information of the target on the container
+ optional int32 grid_x = 12 [default = -1];
+ optional int32 grid_y = 13 [default = -1];
+ optional int32 page_id = 14 [default = -2];
+
+ // e.g., folder icon's (x, y) location and index information on the workspace
+ optional int32 grid_x_parent = 15 [default = -1];
+ optional int32 grid_y_parent = 16 [default = -1];
+ optional int32 page_id_parent = 17 [default = -2];
+
+ // e.g., SEARCHBOX_ALLAPPS, FOLDER_WORKSPACE
+ optional int32 hierarchy = 18;
+
+ optional bool is_work_profile = 19;
+
+ // Used to store the predicted rank of the target
+ optional int32 rank = 20 [default = -1];
+
+ // e.g., folderLabelState can be captured in the following two fields
+ optional int32 from_state = 21;
+ optional int32 to_state = 22;
+
+ // e.g., autofilled or suggested texts that are not user entered
+ optional string edittext = 23;
+
+ // e.g., number of contents inside a container (e.g., icons inside a folder)
+ optional int32 cardinality = 24;
+}
+
+/**
+ * Used for snapshot of the HomeScreen UI elements
+ *
+ * Logged from:
+ * packages/apps/Launcher3
+ */
+message LauncherStaticLayout {
+ // The event id (e.g., snapshot, drag and drop)
+ optional int32 event_id = 1;
+ // The event's source or target id (e.g., icon, shortcut, widget)
+ optional int32 target_id = 2;
+ // If the target needs to be tracked, use this id field
+ optional int32 instance_id = 3;
+ optional int32 uid = 4 [(is_uid) = true];
+ optional string package_name = 5;
+ optional string component_name = 6;
+
+ // (x, y) coordinate and the index information of the target on the container
+ optional int32 grid_x = 7 [default = -1];
+ optional int32 grid_y = 8 [default = -1];
+ optional int32 page_id = 9 [default = -2];
+
+ // e.g., folder icon's (x, y) location and index information on the workspace
+ // e.g., when used with widgets target, use these values for (span_x, span_y)
+ optional int32 grid_x_parent = 10 [default = -1];
+ optional int32 grid_y_parent = 11 [default = -1];
+ optional int32 page_id_parent = 12 [default = -2];
+
+ // UNKNOWN = 0
+ // HOTSEAT = 1
+ // WORKSPACE = 2
+ // FOLDER_HOTSEAT = 3
+ // FOLDER_WORKSPACE = 4
+ optional int32 hierarchy = 13;
+
+ optional bool is_work_profile = 14;
+
+ // e.g., PIN, WIDGET TRAY, APPS TRAY, PREDICTION
+ optional int32 origin = 15;
+
+ // e.g., number of icons inside a folder
+ optional int32 cardinality = 16;
+
+ // e.g., (x, y) span of the widget inside homescreen grid system
+ optional int32 span_x = 17 [default = 1];
+ optional int32 span_y = 18 [default = 1];
}
+/**
+ * Logs when Wallpaper or ThemePicker UI has changed.
+ *
+ * Logged from:
+ * packages/apps/ThemePicker
+ * packages/apps/WallpaperPicker2
+ */
message StyleUIChanged {
optional android.stats.style.Action action = 1;
optional int32 color_package_hash = 2;
@@ -2848,12 +3237,14 @@ message SettingsUIChanged {
message TouchEventReported {
/**
* The fields latency_{min|max|mean|stdev} represent minimum, maximum, mean,
- * and the standard deviation of latency between the kernel and framework
- * for touchscreen events. The units are microseconds.
+ * and the standard deviation of the time spent processing touchscreen events
+ * in the kernel and inputflinger. The units are microseconds.
*
- * The number is measured as the difference between the time at which
- * the input event was received in the evdev driver,
- * and the time at which the input event was received in EventHub.
+ * On supported devices, the starting point is taken during the hard interrupt inside the
+ * kernel touch driver. On all other devices, the starting point is taken inside
+ * the kernel's input event subsystem upon receipt of the input event.
+ * The ending point is taken inside InputDispatcher, just after the input event
+ * is sent to the app.
*/
// Minimum value
optional float latency_min_micros = 1;
@@ -3023,7 +3414,7 @@ message AppCrashOccurred {
optional string process_name = 3;
// The pid if available. -1 means not available.
- optional sint32 pid = 4;
+ optional int32 pid = 4;
optional string package_name = 5;
@@ -3059,7 +3450,7 @@ message WTFOccurred {
optional string process_name = 3;
// The pid if available. -1 means not available.
- optional sint32 pid = 4;
+ optional int32 pid = 4;
optional android.server.ErrorSource error_source = 5;
}
@@ -3295,9 +3686,9 @@ message PictureInPictureStateChanged {
* services/core/java/com/android/server/wm/Session.java
*/
message OverlayStateChanged {
- optional int32 uid = 1 [(is_uid) = true];
+ optional int32 uid = 1 [(state_field_option).primary_field = true, (is_uid) = true];
- optional string package_name = 2;
+ optional string package_name = 2 [(state_field_option).primary_field = true];
optional bool using_alert_window = 3;
@@ -3305,15 +3696,16 @@ message OverlayStateChanged {
ENTERED = 1;
EXITED = 2;
}
- optional State state = 4;
+ optional State state = 4
+ [(state_field_option).exclusive_state = true, (state_field_option).nested = false];
}
-/*
+/**
* Logs foreground service starts and stops.
* Note that this is not when a service starts or stops, but when it is
* considered foreground.
* Logged from
- * //frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
+ * frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
*/
message ForegroundServiceStateChanged {
optional int32 uid = 1 [(is_uid) = true];
@@ -3325,6 +3717,45 @@ message ForegroundServiceStateChanged {
EXIT = 2;
}
optional State state = 3;
+
+ // Whether the fgs is allowed while-in-use permissions, i.e. is considered 'in-use' to the user.
+ // (If the fgs was started while the app wasn't TOP it usually will be denied these permissions)
+ optional bool allow_while_in_use_permission = 4;
+}
+
+/**
+ * Logs the number of times a uid accesses a sensitive AppOp during a foreground service session.
+ * A foreground service session is any continuous period during which the uid holds at least one
+ * foreground service; the atom will be pushed when the uid no longer holds any foreground services.
+ * Accesses initiated while the uid is in the TOP state are ignored.
+ * Sessions with no attempted accesses are not logged.
+ * Logged from
+ * frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
+ */
+message ForegroundServiceAppOpSessionEnded {
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // The operation's name.
+ // Only following four ops are logged
+ // COARSE_LOCATION = 0
+ // FINE_LOCATION = 1
+ // CAMERA = 26
+ // RECORD_AUDIO = 27
+ optional android.app.AppOpEnum app_op_name = 2 [default = APP_OP_NONE];
+
+ // The uid's permission mode for accessing the AppOp during this fgs session.
+ enum Mode {
+ MODE_UNKNOWN = 0;
+ MODE_ALLOWED = 1; // Always allowed
+ MODE_IGNORED = 2; // Denied
+ MODE_FOREGROUND = 3; // Allow-while-in-use (or allowed-one-time)
+ }
+ optional Mode app_op_mode = 3;
+
+ // Number of times this AppOp was requested and allowed.
+ optional int32 count_ops_accepted = 4;
+ // Number of times this AppOp was requested but denied.
+ optional int32 count_ops_rejected = 5;
}
/**
@@ -3471,7 +3902,7 @@ message LmkKillOccurred {
*/
message AppDied {
// timestamp(elapsedRealtime) of record creation
- optional uint64 timestamp_millis = 1 [(state_field_option).option = EXCLUSIVE];
+ optional uint64 timestamp_millis = 1 [(state_field_option).exclusive_state = true];
}
/**
@@ -3486,6 +3917,144 @@ message GenericAtom {
}
/**
+ * Atom for simple logging of user interaction and impression events, such as "the user touched
+ * this button" or "this dialog was displayed".
+ * Keep the UI event stream clean: don't use for system or background events.
+ * Log using the UiEventLogger wrapper - don't write with the StatsLog API directly.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/
+ * frameworks/base/packages/SystemUI/src/com/android/systemui/
+ */
+message UiEventReported {
+ // The event_id.
+ optional int32 event_id = 1;
+ // The event's source or target uid and package, if applicable.
+ // For example, the package posting a notification, or the destination package of a share.
+ optional int32 uid = 2 [(is_uid) = true];
+ optional string package_name = 3;
+ // An identifier used to disambiguate which logs refer to a particular instance of some
+ // UI element. Useful when there might be multiple instances simultaneously active.
+ optional int32 instance_id = 4;
+}
+
+/**
+ * Reports a notification was created or updated.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/notification/
+ */
+message NotificationReported {
+ // The event_id (as for UiEventReported).
+ optional int32 event_id = 1;
+ // The notifying app's uid and package.
+ optional int32 uid = 2 [(is_uid) = true];
+ optional string package_name = 3;
+ // A small system-assigned identifier for the notification.
+ // Locally probably-unique, but expect collisions across users and/or days.
+ optional int32 instance_id = 4;
+ optional int32 notification_id_hash = 5; // Small hash of the app-assigned notif ID + tag
+ optional int32 channel_id_hash = 6; // Small hash of app-assigned channel ID
+
+ // Grouping information
+ optional int32 group_id_hash = 7; // Small hash of the group ID of the notification
+ optional int32 group_instance_id = 8; // Instance_id of the group-summary notification
+ optional bool is_group_summary = 9; // Tags the group-summary notification
+
+ // Attributes
+ optional string category = 10; // App-assigned notification category (API-defined strings)
+ optional int32 style = 11; // App-assigned notification style
+ optional int32 num_people = 12; // Number of Person records attached to the notification
+
+ // Ordering, importance and interruptiveness
+
+ optional int32 position = 13; // Position in NotificationManager's list
+
+ optional android.stats.sysui.NotificationImportance importance = 14;
+ optional int32 alerting = 15; // Bitfield, 1=buzz 2=beep 4=blink
+
+ enum NotificationImportanceExplanation {
+ IMPORTANCE_EXPLANATION_UNKNOWN = 0;
+ IMPORTANCE_EXPLANATION_APP = 1; // App-specified channel importance.
+ IMPORTANCE_EXPLANATION_USER = 2; // User-specified channel importance.
+ IMPORTANCE_EXPLANATION_ASST = 3; // Notification Assistant override.
+ IMPORTANCE_EXPLANATION_SYSTEM = 4; // System override.
+ // Like _APP, but based on pre-channels priority signal.
+ IMPORTANCE_EXPLANATION_APP_PRE_CHANNELS = 5;
+ }
+
+ optional NotificationImportanceExplanation importance_source = 16;
+ optional android.stats.sysui.NotificationImportance importance_initial = 17;
+ optional NotificationImportanceExplanation importance_initial_source = 18;
+ optional android.stats.sysui.NotificationImportance importance_asst = 19;
+ optional int32 assistant_hash = 20;
+ optional float assistant_ranking_score = 21;
+}
+
+message Notification {
+ // The notifying app's uid and package.
+ optional int32 uid = 1 [(is_uid) = true];
+ optional string package_name = 2;
+ // A small system-assigned identifier for the notification.
+ optional int32 instance_id = 3;
+
+ // Grouping information.
+ optional int32 group_instance_id = 4;
+ optional bool is_group_summary = 5;
+
+ // The section of the shade that the notification is in.
+ // See NotificationSectionsManager.PriorityBucket.
+ enum NotificationSection {
+ SECTION_UNKNOWN = 0;
+ SECTION_HEADS_UP = 1;
+ SECTION_MEDIA_CONTROLS = 2;
+ SECTION_PEOPLE = 3;
+ SECTION_ALERTING = 4;
+ SECTION_SILENT = 5;
+ }
+ optional NotificationSection section = 6;
+}
+
+message NotificationList {
+ repeated Notification notifications = 1; // An ordered sequence of notifications.
+}
+
+/**
+ * Reports a notification panel was displayed, e.g. from the lockscreen or status bar.
+ *
+ * Logged from:
+ * frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/
+ */
+message NotificationPanelReported {
+ // The event_id (as for UiEventReported).
+ optional int32 event_id = 1;
+ optional int32 num_notifications = 2;
+ // The notifications in the panel, in the order that they appear there.
+ optional NotificationList notifications = 3 [(log_mode) = MODE_BYTES];
+}
+
+/**
+ * Reports a notification channel, or channel group, was created, updated, or deleted.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/notification/
+ */
+message NotificationChannelModified {
+ // The event_id (as for UiEventReported).
+ optional int32 event_id = 1;
+ // The notifying app's uid and package.
+ optional int32 uid = 2 [(is_uid) = true];
+ optional string package_name = 3;
+ // Hash of app-assigned notification channel ID or channel-group ID
+ optional int32 channel_id_hash = 4;
+ // Previous importance setting, if applicable
+ optional android.stats.sysui.NotificationImportance old_importance = 5;
+ // New importance setting
+ optional android.stats.sysui.NotificationImportance importance = 6;
+}
+
+
+/**
* Logs when a biometric acquire event occurs.
*
* Logged from:
@@ -3622,6 +4191,7 @@ message FlagFlipUpdateOccurred {
/**
* Potential experiment ids that goes with a train install.
+ * Should be kept in sync with experiment_ids.proto.
*/
message TrainExperimentIds {
repeated int64 experiment_id = 1;
@@ -3673,12 +4243,16 @@ message BinaryPushStateChanged {
INSTALL_FAILURE_DOWNLOAD = 23;
INSTALL_FAILURE_STATE_MISMATCH = 24;
INSTALL_FAILURE_COMMIT = 25;
+ REBOOT_TRIGGERED = 26;
}
optional State state = 6;
// Possible experiment ids for monitoring this push.
optional TrainExperimentIds experiment_ids = 7 [(log_mode) = MODE_BYTES];
// user id
optional int32 user_id = 8;
+ optional int32 reason = 9;
+ // Whether or not this is a rollback event
+ optional bool is_rollback = 10;
}
/* Test atom, is not logged anywhere */
@@ -3851,7 +4425,7 @@ message PrivacyIndicatorsInteracted {
DIALOG_LINE_ITEM = 5;
}
- optional Type type = 1 [(state_field_option).option = EXCLUSIVE];
+ optional Type type = 1 [(state_field_option).exclusive_state = true];
// Used if the type is LINE_ITEM
optional string package_name = 2;
@@ -3981,6 +4555,145 @@ message VmsClientConnectionStateChanged {
optional State state = 2;
}
+message MimeTypes {
+ repeated string mime_types = 1;
+}
+
+/**
+ * Logs statistics regarding accesses to external storage.
+ * All stats are normalized for one day period.
+ *
+ * Logged from:
+ * packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java
+ */
+message GeneralExternalStorageAccessStats {
+ optional int32 uid = 1 [(is_uid) = true];
+ // Total number of accesses like creation, open, delete and rename/update.
+ // Includes file path and ContentResolver accesses
+ optional uint32 total_accesses = 2;
+ // Number of file path accesses, as opposed to file path and ContentResolver.
+ optional uint32 file_path_accesses = 3;
+ // Number of accesses on secondary volumes like SD cards.
+ // Includes file path and ContentResolver accesses
+ optional uint32 secondary_storage_accesses = 4;
+ // Comma-separated list of mime types that were accessed.
+ optional MimeTypes mime_types_accessed = 5;
+}
+
+/**
+ * Logs when MediaProvider has successfully finished scanning a storage volume.
+ *
+ * Logged from:
+ * packages/providers/MediaProvider/src/com/android/providers/media/scan/ModernMediaScanner.java
+ */
+message MediaProviderScanOccurred {
+ enum Reason {
+ // Scan triggered due to unknown reason
+ UNKNOWN = 0;
+ // Scan triggered due to storage volume being mounted
+ MOUNTED = 1;
+ // Scan triggered due to explicit user action or app request
+ DEMAND = 2;
+ // Scan triggered due to idle maintenance
+ IDLE = 3;
+ }
+
+ // Volume type that this event pertains to
+ optional android.stats.mediaprovider.VolumeType volume_type = 1;
+ // Reason why this scan was triggered
+ optional Reason reason = 2;
+ // Total number of files scanned
+ optional int64 item_count = 3;
+ // Duration of scan, normalized per file
+ optional float normalized_duration_millis = 4;
+ // Number of database inserts, normalized per file
+ optional float normalized_insert_count = 5;
+ // Number of database updates, normalized per file
+ optional float normalized_update_count = 6;
+ // Number of database deletes, normalized per file
+ optional float normalized_delete_count = 7;
+}
+
+/**
+ * Logs when an app has asked MediaProvider to delete media belonging to the user.
+ *
+ * Logged from:
+ * packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java
+ */
+message MediaContentDeleted {
+ // Volume type that this event pertains to
+ optional android.stats.mediaprovider.VolumeType volume_type = 1;
+ // UID of app that requested deletion
+ optional int32 uid = 2 [(is_uid) = true];
+ // Number of items that were deleted
+ optional int32 item_count = 3;
+}
+
+/**
+ * Logs when an app has asked MediaProvider to grant them access to media belonging to the user.
+ *
+ * Logged from:
+ * packages/providers/MediaProvider/src/com/android/providers/media/PermissionActivity.java
+ */
+message MediaProviderPermissionRequested {
+ enum Result {
+ UNKNOWN = 0;
+ USER_GRANTED = 1;
+ AUTO_GRANTED = 2;
+ USER_DENIED = 3;
+ USER_DENIED_WITH_PREJUDICE = 4;
+ AUTO_DENIED = 5;
+ }
+
+ // Volume type that this event pertains to
+ optional android.stats.mediaprovider.VolumeType volume_type = 1;
+ // UID of app that requested permission
+ optional int32 uid = 2 [(is_uid) = true];
+ // Number of items that were requested
+ optional int32 item_count = 3;
+ // Result of this request
+ optional Result result = 4;
+}
+
+/**
+ * Logs when MediaProvider has finished upgrading or downgrading its database schema.
+ *
+ * Logged from:
+ * packages/providers/MediaProvider/src/com/android/providers/media/DatabaseHelper.java
+ */
+message MediaProviderSchemaChanged {
+ // Volume type that this event pertains to
+ optional android.stats.mediaprovider.VolumeType volume_type = 1;
+ // Old database version code
+ optional int32 version_from = 2;
+ // New database version code
+ optional int32 version_to = 3;
+ // Total number of files in database
+ optional int64 item_count = 4;
+ // Duration of schema change, normalized per file
+ optional float normalized_duration_millis = 5;
+}
+
+/**
+ * Logs when MediaProvider has finished an idle maintenance job.
+ *
+ * Logged from:
+ * packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java
+ */
+message MediaProviderIdleMaintenanceFinished {
+ // Volume type that this event pertains to
+ optional android.stats.mediaprovider.VolumeType volume_type = 1;
+
+ // Total number of files in database
+ optional int64 item_count = 2;
+ // Duration of idle maintenance, normalized per file
+ optional float normalized_duration_millis = 3;
+ // Number of thumbnails found to be stale, normalized per file
+ optional float normalized_stale_thumbnails = 4;
+ // Number of items found to be expired, normalized per file
+ optional float normalized_expired_media = 5;
+}
+
/**
* Represents boot time event with duration in ms.
*
@@ -4190,8 +4903,7 @@ message BootTimeEventErrorCode {
* after an OTA.
*
* Logged from:
- * - system/core/fs_mgr/libsnapshot/snapshot.cpp
- * - system/core/fs_mgr/libsnapshot/snapshotctl.cpp
+ * - system/update_engine/cleanup_previous_update_action.cc
*/
message SnapshotMergeReported {
// Keep in sync with
@@ -4229,6 +4941,106 @@ message SnapshotMergeReported {
// Number of reboots that occurred after issuing and before completing the
// merge of all the snapshot devices.
optional int32 intermediate_reboots = 3;
+
+ // The device has been upgraded to Virtual A/B.
+ optional bool is_vab_retrofit = 4;
+
+ // Space that has been temporarily allocated in the /data partition
+ // containing the dm-snapshot's copy-on-write data generated during a
+ // Virtual A/B update.
+ optional int64 cow_file_size_bytes = 5;
+}
+
+/**
+ * Event representing when BlobStoreManager.Session#commit() is called
+ *
+ * Logged from:
+ * frameworks/base/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+ */
+message BlobCommitted {
+ // Uid of the Blob committer
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // Id of the Blob committed
+ optional int64 blob_id = 2;
+
+ // Size of the Blob
+ optional int64 size = 3;
+
+ enum Result {
+ UNKNOWN = 0;
+ // Commit Succeeded
+ SUCCESS = 1;
+ // Commit Failed: Error occurred during commit
+ ERROR_DURING_COMMIT = 2;
+ // Commit Failed: Digest of the data did not match Blob digest
+ DIGEST_MISMATCH = 3;
+ // Commit Failed: Allowed count limit exceeded
+ COUNT_LIMIT_EXCEEDED = 4;
+ }
+ optional Result result = 4;
+}
+
+/**
+ * Event representing when BlobStoreManager#acquireLease() is called
+ *
+ * Logged from:
+ * frameworks/base/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+ */
+message BlobLeased{
+ // Uid of the Blob leasee
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // Id of the Blob leased or 0 if the Blob does not exist
+ optional int64 blob_id = 2;
+
+ // Size of the Blob or 0 if the Blob does not exist
+ optional int64 size = 3;
+
+ enum Result {
+ UNKNOWN = 0;
+ // Lease Succeeded
+ SUCCESS = 1;
+ // Lease Failed: Blob does not exist
+ BLOB_DNE = 2;
+ // Lease Failed: Leasee does not have access to the Blob
+ ACCESS_NOT_ALLOWED = 3;
+ // Lease Failed: Leasee requested an invalid expiry duration
+ LEASE_EXPIRY_INVALID = 4;
+ // Lease Failed: Leasee has exceeded the total data lease limit
+ DATA_SIZE_LIMIT_EXCEEDED = 5;
+ // Leasee Failed: Allowed count limit exceeded
+ COUNT_LIMIT_EXCEEDED = 6;
+ }
+ optional Result result = 4;
+}
+
+/**
+ * Event representing when BlobStoreManager#openBlob() is called
+ *
+ * Logged from:
+ * frameworks/base/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+ */
+message BlobOpened{
+ // Uid of the Blob opener
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // Id of the Blob opened or 0 if the Blob does not exist
+ optional int64 blob_id = 2;
+
+ // Size of the Blob or 0 if the Blob does not exist
+ optional int64 size = 3;
+
+ enum Result {
+ UNKNOWN = 0;
+ // Open Succeeded
+ SUCCESS = 1;
+ // Open Failed: Blob does not exist
+ BLOB_DNE = 2;
+ // Open Failed: Opener does not have access to the Blob
+ ACCESS_NOT_ALLOWED = 3;
+ }
+ optional Result result = 4;
}
//////////////////////////////////////////////////////////////////////
@@ -4315,6 +5127,66 @@ message MobileBytesTransferByFgBg {
}
/**
+ * Used for pull network statistics via mobile|wifi networks, and sliced by interesting dimensions.
+ * Note that the data is expected to be sliced into more dimensions in future. In other words,
+ * the caller must not assume any row of data is one full report when filtering with a set of
+ * matching conditions, because future data may represent with multiple rows what is currently
+ * represented by one.
+ * To avoid being broken by future slicing, callers must take care to aggregate rows even if they
+ * query all the existing columns.
+ *
+ * Pulled from:
+ * StatsPullAtomService (using NetworkStatsService to get NetworkStats)
+ */
+message DataUsageBytesTransfer {
+ // State of this record. Should be NetworkStats#SET_DEFAULT or NetworkStats#SET_FOREGROUND to
+ // indicate the foreground state, or NetworkStats#SET_ALL to indicate the record is for all
+ // states combined, not including debug states. See NetworkStats#SET_*.
+ optional int32 state = 1;
+
+ optional int64 rx_bytes = 2;
+
+ optional int64 rx_packets = 3;
+
+ optional int64 tx_bytes = 4;
+
+ optional int64 tx_packets = 5;
+
+ // Radio Access Technology (RAT) type of this record, should be one of
+ // TelephonyManager#NETWORK_TYPE_* constants, or NetworkTemplate#NETWORK_TYPE_ALL to indicate
+ // the record is for all rat types combined.
+ optional int32 rat_type = 6;
+
+ // Mcc/Mnc read from sim if the record is for a specific subscription, null indicates the
+ // record is combined across subscriptions.
+ optional string sim_mcc = 7;
+ optional string sim_mnc = 8;
+
+ // Allows mobile virtual network operators (MVNOs) to be identified with individual IDs.
+ // See TelephonyManager#getSimCarrierId.
+ optional int32 carrier_id = 9;
+
+ // Enumeration of opportunistic states with an additional ALL state indicates the record is
+ // combined regardless of the boolean value in its field.
+ enum DataSubscriptionState {
+ UNKNOWN = 0; // For server side backward compatibility.
+ ALL = 1;
+ OPPORTUNISTIC = 2;
+ NOT_OPPORTUNISTIC = 3;
+ }
+ // Mark whether the subscription is an opportunistic data subscription, and ALL indicates the
+ // record is combined across opportunistic data subscriptions.
+ // See {@link SubscriptionManager#setOpportunistic}.
+ optional DataSubscriptionState opportunistic_data_sub = 10;
+
+ // Indicate whether NR is connected, server side could use this with RAT type to determine if
+ // the record is for 5G NSA (Non Stand Alone) mode, where the primary cell is still LTE and
+ // network allocates a secondary 5G cell so telephony reports RAT = LTE along with NR state as
+ // connected.
+ optional bool is_nr_connected = 11;
+}
+
+/**
* Pulls bytes transferred via bluetooth. It is pulled from Bluetooth controller.
*
* Pulled from:
@@ -4521,8 +5393,8 @@ message ProcessMemoryState {
optional int64 page_major_fault = 5;
// RSS
- // Value is read from /proc/PID/status. Or from memory.stat, field
- // total_rss if per-app memory cgroups are enabled.
+ // Value is read from memory.stat, field total_rss if per-app memory
+ // cgroups are enabled. Otherwise, value from /proc/pid/stat.
optional int64 rss_in_bytes = 6;
// CACHE
@@ -4532,67 +5404,52 @@ message ProcessMemoryState {
// SWAP
// Value is read from memory.stat, field total_swap if per-app memory
- // cgroups are enabled. Otherwise, VmSwap from /proc/PID/status.
+ // cgroups are enabled. Otherwise, 0.
optional int64 swap_in_bytes = 8;
- // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always 0.
+ // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always -1.
optional int64 rss_high_watermark_in_bytes = 9 [deprecated = true];
- // Elapsed real time when the process started.
- // Value is read from /proc/PID/stat, field 22. 0 if read from per-app memory cgroups.
- optional int64 start_time_nanos = 10;
+ // Deprecated: use ProcessMemorySnapshot atom instead. Always -1.
+ optional int64 start_time_nanos = 10 [deprecated = true];
- // Anonymous page size plus swap size. Values are read from /proc/PID/status.
- optional int32 anon_rss_and_swap_in_kilobytes = 11;
+ // Deprecated: use ProcessMemorySnapshot atom instead. Always -1.
+ optional int32 anon_rss_and_swap_in_kilobytes = 11 [deprecated = true];
}
/*
- * Logs the memory stats for a native process (from procfs).
+ * Logs the memory high-water mark for a process.
*
- * Pulled from StatsCompanionService for selected native processes.
+ * Pulled from StatsCompanionService for all managed processes (from ActivityManagerServie)
+ * and for selected native processes.
+ *
+ * Pulling this atom resets high-water mark counters for all processes.
*/
-message NativeProcessMemoryState {
+message ProcessMemoryHighWaterMark {
// The uid if available. -1 means not available.
optional int32 uid = 1 [(is_uid) = true];
// The process name.
- // Value read from /proc/PID/cmdline.
+ // Usually package name or process cmdline.
+ // Provided by ActivityManagerService or read from /proc/PID/cmdline.
optional string process_name = 2;
- // # of page-faults
- optional int64 page_fault = 3;
-
- // # of major page-faults
- optional int64 page_major_fault = 4;
-
- // RSS
- // Value read from /proc/PID/status.
- optional int64 rss_in_bytes = 5;
-
- // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always 0.
- optional int64 rss_high_watermark_in_bytes = 6 [deprecated = true];
-
- // Elapsed real time when the process started.
- // Value is read from /proc/PID/stat, field 22.
- optional int64 start_time_nanos = 7;
-
- // SWAP
- // Value read from /proc/PID/status, field VmSwap.
- optional int64 swap_in_bytes = 8;
+ // Deprecated: use rss_high_water_mark_in_kilobytes instead. This field is
+ // computed by converting kilobytes to bytes.
+ optional int64 rss_high_water_mark_in_bytes = 3 [deprecated = true];
- // Anonymous page size plus swap size. Values are read from /proc/PID/status.
- optional int32 anon_rss_and_swap_in_kilobytes = 9;
+ // RSS high-water mark. Peak RSS usage of the process. Read from the VmHWM field in
+ // /proc/PID/status.
+ optional int32 rss_high_water_mark_in_kilobytes = 4;
}
/*
- * Logs the memory high-water mark for a process.
+ * Logs the memory stats for a process.
*
- * Pulled from StatsCompanionService for all managed processes (from ActivityManagerServie)
+ * Pulled from StatsCompanionService for all managed processes (from ActivityManagerService)
* and for selected native processes.
- *
- * Pulling this atom resets high-water mark counters for all processes.
*/
-message ProcessMemoryHighWaterMark {
+message ProcessMemorySnapshot {
// The uid if available. -1 means not available.
optional int32 uid = 1 [(is_uid) = true];
@@ -4601,9 +5458,29 @@ message ProcessMemoryHighWaterMark {
// Provided by ActivityManagerService or read from /proc/PID/cmdline.
optional string process_name = 2;
- // RSS high-water mark. Peak RSS usage of the process. Read from the VmHWM field in
- // /proc/PID/status.
- optional int64 rss_high_water_mark_in_bytes = 3;
+ // The pid of the process.
+ // Allows to disambiguate instances of the process.
+ optional int32 pid = 3;
+
+ // The current OOM score adjustment value.
+ // Read from ProcessRecord for managed processes.
+ // Placeholder -1001 (OOM_SCORE_ADJ_MIN - 1, outside of allowed range) for native ones.
+ optional int32 oom_score_adj = 4;
+
+ // The current RSS of the process.
+ // VmRSS from /proc/pid/status.
+ optional int32 rss_in_kilobytes = 5;
+
+ // The current anon RSS of the process.
+ // RssAnon from /proc/pid/status.
+ optional int32 anon_rss_in_kilobytes = 6;
+
+ // The current swap size of the process.
+ // VmSwap from /proc/pid/status.
+ optional int32 swap_in_kilobytes = 7;
+
+ // The sum of rss_in_kilobytes and swap_in_kilobytes.
+ optional int32 anon_rss_and_swap_in_kilobytes = 8;
}
/*
@@ -5026,48 +5903,81 @@ message RoleHolder {
}
message AggStats {
- optional int64 min = 1;
-
- optional int64 average = 2;
-
- optional int64 max = 3;
-}
-
+ // These are all in byte resolution.
+ optional int64 min = 1 [deprecated = true];
+ optional int64 average = 2 [deprecated = true];
+ optional int64 max = 3 [deprecated = true];
+
+ // These are all in kilobyte resolution. Can fit in int32, so smaller on the wire than the above
+ // int64 fields.
+ optional int32 mean_kb = 4;
+ optional int32 max_kb = 5;
+}
+
+// A reduced subset of process states; reducing the number of possible states allows more
+// aggressive device-side aggregation of statistics and hence reduces metric upload size.
+enum ProcessStateAggregated {
+ PROCESS_STATE_UNKNOWN = 0;
+ // Persistent system process.
+ PROCESS_STATE_PERSISTENT = 1;
+ // Top activity; actually any visible activity.
+ PROCESS_STATE_TOP = 2;
+ // Process binding to top or a foreground service.
+ PROCESS_STATE_BOUND_TOP_OR_FGS = 3;
+ // Processing running a foreground service.
+ PROCESS_STATE_FGS = 4;
+ // Important foreground process (ime, wallpaper, etc).
+ PROCESS_STATE_IMPORTANT_FOREGROUND = 5;
+ // Important background process.
+ PROCESS_STATE_BACKGROUND = 6;
+ // Process running a receiver.
+ PROCESS_STATE_RECEIVER = 7;
+ // All kinds of cached processes.
+ PROCESS_STATE_CACHED = 8;
+}
+
+// Next tag: 13
message ProcessStatsStateProto {
optional android.service.procstats.ScreenState screen_state = 1;
- optional android.service.procstats.MemoryState memory_state = 2;
+ optional android.service.procstats.MemoryState memory_state = 2 [deprecated = true];
// this enum list is from frameworks/base/core/java/com/android/internal/app/procstats/ProcessStats.java
// and not frameworks/base/core/java/android/app/ActivityManager.java
- optional android.service.procstats.ProcessState process_state = 3;
+ optional android.service.procstats.ProcessState process_state = 3 [deprecated = true];
+
+ optional ProcessStateAggregated process_state_aggregated = 10;
// Millisecond uptime duration spent in this state
- optional int64 duration_millis = 4;
+ optional int64 duration_millis = 4 [deprecated = true];
+ // Same as above, but with minute resolution so it fits into an int32.
+ optional int32 duration_minutes = 11;
// Millisecond elapsed realtime duration spent in this state
- optional int64 realtime_duration_millis = 9;
+ optional int64 realtime_duration_millis = 9 [deprecated = true];
+ // Same as above, but with minute resolution so it fits into an int32.
+ optional int32 realtime_duration_minutes = 12;
// # of samples taken
optional int32 sample_size = 5;
// PSS is memory reserved for this process
- optional AggStats pss = 6;
+ optional AggStats pss = 6 [deprecated = true];
// USS is memory shared between processes, divided evenly for accounting
- optional AggStats uss = 7;
+ optional AggStats uss = 7 [deprecated = true];
// RSS is memory resident for this process
optional AggStats rss = 8;
}
-// Next Tag: 7
+// Next Tag: 8
message ProcessStatsProto {
// Name of process.
optional string process = 1;
// Uid of the process.
- optional int32 uid = 2;
+ optional int32 uid = 2 [(is_uid) = true];
// Information about how often kills occurred
message Kill {
@@ -5080,7 +5990,7 @@ message ProcessStatsProto {
// PSS stats during cached kill
optional AggStats cached_pss = 3;
}
- optional Kill kill = 3;
+ optional Kill kill = 3 [deprecated = true];
// Time and memory spent in various states.
repeated ProcessStatsStateProto states = 5;
@@ -5088,6 +5998,28 @@ message ProcessStatsProto {
// Total time process has been running... screen_state, memory_state, and process_state
// will not be set.
optional ProcessStatsStateProto total_running_state = 6;
+
+ // Association data for this process in this state;
+ // each entry here is one association.
+ repeated ProcessStatsAssociationProto assocs = 7;
+}
+
+// Next Tag: 6
+message ProcessStatsAssociationProto {
+ // Procss Name of the associated process (client process of service binding)
+ optional string assoc_process_name = 1;
+
+ // Package Name of the associated package (client package of service binding)
+ optional string assoc_package_name = 2 [deprecated = true];
+
+ // UID of the associated process/package (client package of service binding)
+ optional int32 assoc_uid = 5 [(is_uid) = true];
+
+ // Total count of the times this association (service binding) appeared.
+ optional int32 total_count = 3;
+
+ // Uptime total duration in seconds this association (service binding) was around.
+ optional int32 total_duration_secs = 4;
}
message PackageServiceOperationStatsProto {
@@ -5236,6 +6168,10 @@ message ProcessStatsAvailablePagesProto {
*/
message ProcStats {
optional ProcessStatsSectionProto proc_stats_section = 1;
+ // Data pulled from device into this is sometimes sharded across multiple atoms to work around
+ // a size limit. When this happens, this shard ID will contain an increasing 1-indexed integer
+ // with the number of this shard.
+ optional int32 shard_id = 2;
}
/**
@@ -5263,6 +6199,141 @@ message NotificationRemoteViews {
optional NotificationRemoteViewsProto notification_remote_views = 1;
}
+/**
+ * Atom that contains a list of a package's preferences, pulled from NotificationManagerService.java
+ */
+message PackageNotificationPreferences {
+ // Uid under which the package is installed.
+ optional int32 uid = 1 [(is_uid) = true];
+ // Notification importance, which specifies when and how a notification is displayed.
+ // Specified under core/java/android/app/NotificationManager.java.
+ optional int32 importance = 2;
+ // Lockscreen visibility as set by the user.
+ optional int32 visibility = 3;
+ // Bitfield mask indicating what fields were locked by the user (see LockableAppfields in
+ // PreferencesHelper.java)
+ optional int32 user_locked_fields = 4;
+}
+
+/**
+ * Atom that contains a list of a package's channel preferences, pulled from
+ * NotificationManagerService.java.
+ */
+message PackageNotificationChannelPreferences {
+ // Uid under which the package is installed.
+ optional int32 uid = 1 [(is_uid) = true];
+ // Channel's ID. Should always be available.
+ optional string channel_id = 2;
+ // Channel's name. Should always be available.
+ optional string channel_name = 3;
+ // Channel's description. Optionally set by the channel creator.
+ optional string description = 4;
+ // Notification importance, which specifies when and how a notification is displayed. Specified
+ // under core/java/android/app/NotificationManager.java.
+ optional int32 importance = 5;
+ // Bitmask representing which fields have been set by the user. See field bitmask descriptions
+ // at core/java/android/app/NotificationChannel.java
+ optional int32 user_locked_fields = 6;
+ // Indicates if the channel was deleted by the app.
+ optional bool is_deleted = 7;
+ // Indicates if the channel was marked as a conversation by the app.
+ optional bool is_conversation = 8;
+ // Indicates if the channel is a conversation that was demoted by the user.
+ optional bool is_demoted_conversation = 9;
+ // Indicates if the channel is a conversation that was marked as important by the user.
+ optional bool is_important_conversation = 10;
+}
+
+/**
+ * Atom that represents an item in the list of Do Not Disturb rules, pulled from
+ * NotificationManagerService.java.
+ */
+message DNDModeProto {
+ enum Mode {
+ ROOT_CONFIG = -1; // Used to distinguish the config (one per user) from the rules.
+ ZEN_MODE_OFF = 0;
+ ZEN_MODE_IMPORTANT_INTERRUPTIONS = 1;
+ ZEN_MODE_NO_INTERRUPTIONS = 2;
+ ZEN_MODE_ALARMS = 3;
+ }
+ optional int32 user = 1; // Android user ID (0, 1, 10, ...)
+ optional bool enabled = 2; // true for ROOT_CONFIG if a manualRule is enabled
+ optional bool channels_bypassing = 3; // only valid for ROOT_CONFIG
+ optional Mode zen_mode = 4;
+ // id is one of the system default rule IDs, or empty
+ // May also be "MANUAL_RULE" to indicate app-activation of the manual rule.
+ optional string id = 5;
+ optional int32 uid = 6 [(is_uid) = true]; // currently only SYSTEM_UID or 0 for other
+ optional DNDPolicyProto policy = 7;
+}
+
+/**
+ * Atom that represents a Do Not Disturb policy, an optional detail proto for DNDModeProto.
+ */
+message DNDPolicyProto {
+ enum State {
+ STATE_UNSET = 0;
+ STATE_ALLOW = 1;
+ STATE_DISALLOW = 2;
+ }
+ optional State calls = 1;
+ optional State repeat_callers = 2;
+ optional State messages = 3;
+ optional State conversations = 4;
+ optional State reminders = 5;
+ optional State events = 6;
+ optional State alarms = 7;
+ optional State media = 8;
+ optional State system = 9;
+ optional State fullscreen = 10;
+ optional State lights = 11;
+ optional State peek = 12;
+ optional State status_bar = 13;
+ optional State badge = 14;
+ optional State ambient = 15;
+ optional State notification_list = 16;
+
+ enum PeopleType {
+ PEOPLE_UNSET = 0;
+ PEOPLE_ANYONE = 1;
+ PEOPLE_CONTACTS = 2;
+ PEOPLE_STARRED = 3;
+ PEOPLE_NONE = 4;
+ }
+
+ optional PeopleType allow_calls_from = 17;
+ optional PeopleType allow_messages_from = 18;
+
+ enum ConversationType {
+ CONV_UNSET = 0;
+ CONV_ANYONE = 1;
+ CONV_IMPORTANT = 2;
+ CONV_NONE = 3;
+ }
+
+ optional ConversationType allow_conversations_from = 19;
+}
+
+/**
+ * Atom that contains a list of a package's channel group preferences, pulled from
+ * NotificationManagerService.java.
+ */
+message PackageNotificationChannelGroupPreferences {
+ // Uid under which the package is installed.
+ optional int32 uid = 1 [(is_uid) = true];
+ // Channel Group's ID. Should always be available.
+ optional string group_id = 2;
+ // Channel Group's name. Should always be available.
+ optional string group_name = 3;
+ // Channel Group's description. Optionally set by group creator.
+ optional string description = 4;
+ // Indicates if notifications from this channel group are blocked.
+ optional bool is_blocked = 5;
+ // Bitmask representing which fields have been set by the user. See field bitmask descriptions
+ // at core/java/android/app/NotificationChannelGroup.java
+ optional int32 user_locked_fields = 6;
+}
+
message PowerProfileProto {
optional double cpu_suspend = 1;
@@ -5494,6 +6565,16 @@ message ContentCaptureServiceEvents {
SET_WHITELIST = 3;
SET_DISABLED = 4;
ON_USER_DATA_REMOVED = 5;
+ ON_DATA_SHARE_REQUEST = 6;
+ ACCEPT_DATA_SHARE_REQUEST = 7;
+ REJECT_DATA_SHARE_REQUEST = 8;
+ DATA_SHARE_WRITE_FINISHED = 9;
+ DATA_SHARE_ERROR_IOEXCEPTION = 10;
+ DATA_SHARE_ERROR_EMPTY_DATA = 11;
+ DATA_SHARE_ERROR_CLIENT_PIPE_FAIL = 12;
+ DATA_SHARE_ERROR_SERVICE_PIPE_FAIL = 13;
+ DATA_SHARE_ERROR_CONCURRENT_REQUEST = 14;
+ DATA_SHARE_ERROR_TIMEOUT_INTERRUPTED = 15;
}
optional Event event = 1;
// component/package of content capture service.
@@ -5785,6 +6866,15 @@ message DocsUIPickResultReported {
optional int32 repeatedly_pick_times = 7;
}
+/** Logs the drag and drop of files.
+
+ * Logged from:
+ * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+ */
+message DocsUIDragAndDropReported {
+ optional bool drag_initiated_from_docsui = 1;
+}
+
/**
* Logs when an app's memory is compacted.
*
@@ -6431,10 +7521,10 @@ message PermissionGrantRequestResultReported {
optional int64 request_id = 1;
// UID of package requesting the permission grant
- optional int32 requesting_uid = 2 [(is_uid) = true];
+ optional int32 uid = 2 [(is_uid) = true];
// Name of package requesting the permission grant
- optional string requesting_package_name = 3;
+ optional string package_name = 3;
// The permission to be granted
optional string permission_name = 4;
@@ -6462,6 +7552,20 @@ message PermissionGrantRequestResultReported {
AUTO_DENIED = 8;
// permission request was ignored because permission is restricted
IGNORED_RESTRICTED_PERMISSION = 9;
+ // one time permission was granted by user action
+ USER_GRANTED_ONE_TIME = 10;
+ // user ignored request by leaving the request screen without choosing any option
+ USER_IGNORED = 11;
+ // user granted the permission after being linked to settings
+ USER_GRANTED_IN_SETTINGS = 12;
+ // user denied the permission after being linked to settings
+ USER_DENIED_IN_SETTINGS = 13;
+ // user denied the permission with prejudice after being linked to settings
+ USER_DENIED_WITH_PREJUDICE_IN_SETTINGS = 14;
+ // permission was automatically revoked after one-time permission expired
+ AUTO_ONE_TIME_PERMISSION_REVOKED = 15;
+ // permission was automatically revoked for unused app
+ AUTO_UNUSED_APP_PERMISSION_REVOKED = 16;
}
// The result of the permission grant
optional Result result = 6;
@@ -6922,8 +8026,58 @@ message MediametricsNuPlayerReported {
}
/**
- * State of a dangerous permission requested by a package
+ * Track Legacy DRM usage
+ * Logged from
+ * frameworks/av/drm/drmserver/DrmManager.cpp
*/
+message MediametricsDrmManagerReported {
+ optional int64 timestamp_nanos = 1;
+ optional string package_name = 2;
+ optional int64 package_version_code = 3;
+ optional int64 media_apex_version = 4;
+
+ enum Method {
+ METHOD_NOT_FOUND = -1;
+ GET_CONSTRAINTS = 0;
+ GET_METADATA = 1;
+ CAN_HANDLE = 2;
+ PROCESS_DRM_INFO = 3;
+ ACQUIRE_DRM_INFO = 4;
+ SAVE_RIGHTS = 5;
+ GET_ORIGINAL_MIME_TYPE = 6;
+ GET_DRM_OBJECT_TYPE = 7;
+ CHECK_RIGHTS_STATUS = 8;
+ REMOVE_RIGHTS = 9;
+ REMOVE_ALL_RIGHTS = 10;
+ OPEN_CONVERT_SESSION = 11;
+ OPEN_DECRYPT_SESSION = 12;
+ }
+
+ // plugin_id+description inform which Legacy DRM plugins are still in use on device
+ optional string plugin_id = 5;
+ optional string description = 6;
+ optional Method method = 7;
+ optional string mime_types = 8;
+
+ optional int64 get_constraints_count = 9;
+ optional int64 get_metadata_count = 10;
+ optional int64 can_handle_count = 11;
+ optional int64 process_drm_info_count = 12;
+ optional int64 acquire_drm_info_count = 13;
+ optional int64 save_rights_count = 14;
+ optional int64 get_original_mime_type_count = 15;
+ optional int64 get_drm_object_type_count = 16;
+ optional int64 check_rights_status_count = 17;
+ optional int64 remove_rights_count = 18;
+ optional int64 remove_all_rights_count = 19;
+ optional int64 open_convert_session_count = 20;
+ optional int64 open_decrypt_session_count = 21;
+}
+
+/**
+ * State of a dangerous permission requested by a package
+ * Pulled from: StatsCompanionService
+*/
message DangerousPermissionState {
// Name of the permission
optional string permission_name = 1;
@@ -6955,7 +8109,8 @@ message DeviceIdentifierAccessDenied {
optional string method_name = 2;
// True if the package is preinstalled.
- optional bool is_preinstalled = 3;
+ // Starting from Android 11, this boolean is not set and will always be false.
+ optional bool is_preinstalled = 3 [deprecated = true];
// True if the package is privileged.
// Starting from Android 11, this boolean is not set and will always be false.
@@ -7003,6 +8158,7 @@ message TrainInfo {
INSTALL_FAILURE_DOWNLOAD = 23;
INSTALL_FAILURE_STATE_MISMATCH = 24;
INSTALL_FAILURE_COMMIT = 25;
+ REBOOT_TRIGGERED = 26;
}
optional Status status = 4;
}
@@ -7131,6 +8287,12 @@ message GpuStatsAppInfo {
// CPU Vulkan implementation is in use.
optional bool cpu_vulkan_in_use = 6;
+
+ // App is not doing pre-rotation correctly.
+ optional bool false_prerotation = 7;
+
+ // App creates GLESv1 context.
+ optional bool gles_1_in_use = 8;
}
/*
@@ -7139,11 +8301,27 @@ message GpuStatsAppInfo {
* Pulled from StatsCompanionService.
*/
message SystemIonHeapSize {
+ // Deprecated due to limited support of ion stats in debugfs.
+ // Use `IonHeapSize` instead.
+ option deprecated = true;
+
// Size of the system ion heap in bytes.
+ // Read from debugfs.
optional int64 size_in_bytes = 1;
}
/*
+ * Logs the total size of the ion heap.
+ *
+ * Pulled from StatsCompanionService.
+ */
+message IonHeapSize {
+ // Total size of all ion heaps in kilobytes.
+ // Read from: /sys/kernel/ion/total_heaps_kb.
+ optional int32 total_size_kb = 1;
+}
+
+/*
* Logs the per-process size of the system ion heap.
*
* Pulled from StatsCompanionService.
@@ -7234,8 +8412,14 @@ message CoolingDevice {
* Logged from the Intelligence mainline module.
*/
message IntelligenceEventReported {
+ // The event type.
optional android.stats.intelligence.EventType event_id = 1;
+ // Success, failure.
optional android.stats.intelligence.Status status = 2;
+ // How many times the event occured (to report a batch of high frequency events).
+ optional int32 count = 3;
+ // How long the event took (sum of durations if count > 1)
+ optional int64 duration_millis = 4;
}
/**
@@ -7258,6 +8442,245 @@ message CarPowerStateChanged {
}
/**
+ * Logs when Car User Hal is requested to switch/create/remove user.
+ *
+ * Logged from:
+ * packages/services/Car/service/src/com/android/car/hal/UserHalService.java
+ */
+message CarUserHalModifyUserRequestReported {
+ // Request id for the request.
+ optional int32 request_id = 1;
+ // Request type.
+ enum RequestType {
+ UNKNOWN = 0;
+ // Car user manager requested user switch.
+ SWITCH_REQUEST_ANDROID = 1;
+ // OEM requested User switch.
+ SWITCH_REQUEST_OEM = 2;
+ // Hal switch requested after android switch using activity manager.
+ SWITCH_REQUEST_LEGACY = 3;
+ // Create User
+ CREATE_REQUEST = 4;
+ // Remove User
+ REMOVE_REQUEST = 5;
+ }
+ optional RequestType request_type = 2;
+ // Android User id of the current user which can only be 0, 10, 11 and so on.
+ // -1 if not available.
+ optional int32 user_id = 3;
+ // VHAL flags of the current user. (-1 if not available)
+ optional int32 user_flags = 4;
+ // Android User id of the target user for switch/create/remove. It can only
+ // be 0, 10, 11 and so on. -1 if not available.
+ optional int32 target_user_id = 5;
+ // VHAL flags of the target user for switch/create/remove. (-1 if not available)
+ optional int32 target_user_flags = 6;
+ // Request timeout Milliseconds (-1 if not available)
+ optional int32 timeout_millis = 7;
+}
+
+/**
+ * Logs when Car User Hal responds to switch/create user request.
+ *
+ * Logged from:
+ * packages/services/Car/service/src/com/android/car/hal/UserHalService.java
+ */
+message CarUserHalModifyUserResponseReported {
+ // Request id of the request associated with the response.
+ optional int32 request_id = 1;
+ // Car user hal callback status.
+ enum CallbackStatus {
+ UNKNOWN = 0;
+ // Hal response was invalid.
+ INVALID = 1;
+ // Hal response was ok.
+ OK = 2;
+ // Hal timeout during set call.
+ HAL_SET_TIMEOUT = 3;
+ // Hal response timeout.
+ HAL_RESPONSE_TIMEOUT = 4;
+ // Hal responded with wrong info.
+ WRONG_HAL_RESPONSE = 5;
+ // Hal is processing multiple requests simultaneously.
+ CONCURRENT_OPERATION = 6;
+ }
+ optional CallbackStatus callback_status = 2;
+
+ // Hal request status for user switch/create/remove.
+ enum HalRequestStatus {
+ UNSPECIFIED = 0;
+ // Hal request for user switch/create is successful.
+ SUCCESS = 1;
+ // Hal request for user switch/create failed.
+ FAILURE = 2;
+ }
+ optional HalRequestStatus request_status = 3;
+}
+
+/**
+ * Logs when post switch response is posted to Car User Hal.
+ *
+ * Logged from:
+ * packages/services/Car/service/src/com/android/car/hal/UserHalService.java
+ */
+message CarUserHalPostSwitchResponseReported {
+ // Request id.
+ optional int32 request_id = 1;
+
+ // Android user switch status.
+ enum UserSwitchStatus {
+ UNKNOWN = 0;
+ // Android user switch is successful.
+ SUCCESS = 1;
+ // Android user switch failed.
+ FAILURE = 2;
+ }
+ optional UserSwitchStatus switch_status = 2;
+}
+
+/**
+ * Logs when initial user information is requested from Car User Hal.
+ *
+ * Logged from:
+ * packages/services/Car/service/src/com/android/car/hal/UserHalService.java
+ */
+message CarUserHalInitialUserInfoRequestReported {
+ // Request id for the request.
+ optional int32 request_id = 1;
+
+ // Request type for initial user information.
+ enum InitialUserInfoRequestType {
+ UNKNOWN = 0;
+ // At the first time Android was booted (or after a factory reset).
+ FIRST_BOOT = 1;
+ // At the first time Android was booted after the system was updated.
+ FIRST_BOOT_AFTER_OTA = 2;
+ // When Android was booted "from scratch".
+ COLD_BOOT = 3;
+ // When Android was resumed after the system was suspended to memory.
+ RESUME = 4;
+ }
+ optional InitialUserInfoRequestType request_type = 2;
+ // Request timeout Milliseconds (-1 if not available)
+ optional int32 timeout_millis = 3;
+}
+
+/**
+ * Logs when Car User Hal responds to initial user information requests.
+ *
+ * Logged from:
+ * packages/services/Car/service/src/com/android/car/hal/UserHalService.java
+ */
+message CarUserHalInitialUserInfoResponseReported {
+ // Request id of the request associated with the response.
+ optional int32 request_id = 1;
+ // Car user hal callback status.
+ enum CallbackStatus {
+ UNKNOWN = 0;
+ // Hal response was invalid.
+ INVALID = 1;
+ // Hal response was ok.
+ OK = 2;
+ // Hal timeout during set call.
+ HAL_SET_TIMEOUT = 3;
+ // Hal response timeout.
+ HAL_RESPONSE_TIMEOUT = 4;
+ // Hal responded with wrong info.
+ WRONG_HAL_RESPONSE = 5;
+ // Hal is processing multiple requests simultaneously.
+ CONCURRENT_OPERATION = 6;
+ }
+ optional CallbackStatus callback_status = 2;
+ // Response for initial user information request.
+ enum InitialUserInfoResponseAction {
+ UNSPECIFIED = 0;
+ // Let the Android System decide what to do.
+ DEFAULT = 1;
+ // Switch to an existing Android user.
+ SWITCH = 2;
+ // Create a new Android user (and switch to it).
+ CREATE = 3;
+ }
+ optional InitialUserInfoResponseAction response_action = 3;
+ // Android User id of the target user which can only be 0, 10, 11 and so on.
+ // -1 if not available.
+ optional int32 target_user = 4;
+ // VHAL flags of the current user. (-1 if not available)
+ optional int32 target_user_flags = 5;
+ // User locales
+ optional string user_locales = 6;
+}
+
+/**
+ * Logs when set user association is requested from Car User Hal.
+ *
+ * Logged from:
+ * packages/services/Car/service/src/com/android/car/hal/UserHalService.java
+ */
+message CarUserHalUserAssociationRequestReported {
+ // Request id for the request.
+ optional int32 request_id = 1;
+ // Request type.
+ enum RequestType {
+ UNKNOWN = 0;
+ // For setting user association information.
+ SET = 1;
+ // For getting user association information.
+ GET = 2;
+ }
+ optional RequestType request_type = 2;
+ // Android User id of the current user which can only be 0, 10, 11 and so on.
+ // -1 if not available.
+ optional int32 current_user_id = 3;
+ // VHAL flags of the current user. (-1 if not available)
+ optional int32 current_user_flags = 4;
+ // Number of the set associations requested.
+ optional int32 number_associations = 5;
+ // Concatenated string for the types from set associations request.
+ // This is a string converted from an array of integers.
+ optional string user_identification_association_types = 6;
+ // Concatenated string for the values from set associations request.
+ // This is a string converted from an array of integers.
+ optional string user_identification_association_values = 7;
+}
+
+/**
+ * Logs when Car User Hal responds to set user association requests.
+ *
+ * Logged from:
+ * packages/services/Car/service/src/com/android/car/hal/UserHalService.java
+ */
+message CarUserHalSetUserAssociationResponseReported {
+ // Request id of the request associated with the response.
+ optional int32 request_id = 1;
+ // Car user hal callback status.
+ enum CallbackStatus {
+ UNKNOWN = 0;
+ // Hal response was invalid.
+ INVALID = 1;
+ // Hal response was ok.
+ OK = 2;
+ // Hal timeout during set call.
+ HAL_SET_TIMEOUT = 3;
+ // Hal response timeout.
+ HAL_RESPONSE_TIMEOUT = 4;
+ // Hal responded with wrong info.
+ WRONG_HAL_RESPONSE = 5;
+ // Hal is processing multiple requests simultaneously.
+ CONCURRENT_OPERATION = 6;
+ }
+ optional CallbackStatus callback_status = 2;
+ // Number of the set associations in the response.
+ optional int32 number_associations = 3;
+ // Concatenated string for the types from set associations request.
+ // This is a string converted from an array of integers.
+ optional string user_identification_association_types = 4;
+ // Concatenated string for the values from set associations request.
+ // This is a string converted from an array of integers.
+ optional string user_identification_association_values = 5;
+}
+
+/**
* Logs whether GarageMode is entered.
*
* Logged from:
@@ -7275,11 +8698,11 @@ message AppOps {
// Uid of the package requesting the op
optional int32 uid = 1 [(is_uid) = true];
- // Nmae of the package performing the op
+ // Name of the package performing the op
optional string package_name = 2;
- // operation id; maps to the OP_* constants in AppOpsManager.java
- optional int32 op_id = 3;
+ // operation id
+ optional android.app.AppOpEnum op_id = 3 [default = APP_OP_NONE];
// The number of times the op was granted while the app was in the
// foreground (only for trusted requests)
@@ -7304,6 +8727,58 @@ message AppOps {
// For long-running operations, total duration of the operation
// while the app was in the background (only for trusted requests)
optional int64 trusted_background_duration_millis = 9;
+
+ // Whether AppOps is guarded by Runtime permission
+ optional bool is_runtime_permission = 10;
+}
+
+/**
+ * Historical app ops data per package and attribution tag.
+ */
+message AttributedAppOps {
+ // Uid of the package requesting the op
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // Name of the package performing the op
+ optional string package_name = 2;
+
+ // tag; provided by developer when accessing related API, limited at 50 chars by API.
+ // Attributions must be provided through manifest using <attribution> tag available in R and
+ // above.
+ optional string tag = 3;
+
+ // operation id
+ optional android.app.AppOpEnum op = 4 [default = APP_OP_NONE];
+
+ // The number of times the op was granted while the app was in the
+ // foreground (only for trusted requests)
+ optional int64 trusted_foreground_granted_count = 5;
+
+ // The number of times the op was granted while the app was in the
+ // background (only for trusted requests)
+ optional int64 trusted_background_granted_count = 6;
+
+ // The number of times the op was rejected while the app was in the
+ // foreground (only for trusted requests)
+ optional int64 trusted_foreground_rejected_count = 7;
+
+ // The number of times the op was rejected while the app was in the
+ // background (only for trusted requests)
+ optional int64 trusted_background_rejected_count = 8;
+
+ // For long-running operations, total duration of the operation
+ // while the app was in the foreground (only for trusted requests)
+ optional int64 trusted_foreground_duration_millis = 9;
+
+ // For long-running operations, total duration of the operation
+ // while the app was in the background (only for trusted requests)
+ optional int64 trusted_background_duration_millis = 10;
+
+ // Whether AppOps is guarded by Runtime permission
+ optional bool is_runtime_permission = 11;
+
+ // Sampling rate used on device, from 0 to 100
+ optional int32 sampling_rate = 12;
}
/**
@@ -7418,6 +8893,9 @@ message GrantPermissionsActivityButtonActions {
// Button clicked by user - same as bit flags in buttons_presented with only single bit set
optional int32 button_clicked = 5;
+
+ // id which identifies single session of user interacting with permission controller
+ optional int64 session_id = 6;
}
/**
@@ -7470,6 +8948,28 @@ message AppPermissionFragmentActionReported {
// The result of the permission grant
optional bool permission_granted = 6;
+
+ // State of Permission Flags after grant as per android.content.pm.PermissionFlags
+ optional int32 permission_flags = 7;
+
+ enum Button {
+ UNDEFINED = 0;
+ // Allow button
+ ALLOW = 1;
+ // Deny button
+ DENY = 2;
+ // Ask every time button
+ ASK_EVERY_TIME = 3;
+ // Allow all the time button
+ ALLOW_ALWAYS = 4;
+ // Allow only while using the app button
+ ALLOW_FOREGROUND = 5;
+ // Same is Deny button but shown in while in use dialog
+ DENY_FOREGROUND = 6;
+ }
+
+ // Button pressed in the dialog
+ optional Button button_pressed = 8;
}
/**
@@ -7490,7 +8990,8 @@ message AppPermissionFragmentViewed {
}
/**
-* Information about a AppPermissionsFragment viewed by user
+* Information about a AppPermissionGroupsFragment viewed by user. Fragment has been renamed, but
+* the log retains the old fragment name.
*/
message AppPermissionsFragmentViewed {
// id which identifies single session of user interacting with permission controller
@@ -7518,7 +9019,6 @@ message AppPermissionsFragmentViewed {
}
optional Category category = 6;
}
-
/**
* Information about a PermissionAppsFragment viewed by user.
* Logged from ui/handheld/PermissionAppsFragment.java
@@ -7551,6 +9051,478 @@ message PermissionAppsFragmentViewed {
}
/**
+* Log that the Auto Revoke notification has been clicked
+* Logged from ui/ManagePermissionsActivity
+*/
+message AutoRevokeNotificationClicked {
+ // id which identifies single session of user interacting with permission controller
+ optional int64 session_id = 1;
+}
+
+/**
+* Log that an app has been displayed on the auto revoke page, and lists one permission that was
+* auto revoked for it.
+* Logged from ui/handheld/AutoRevokeFragment
+*/
+message AutoRevokeFragmentAppViewed {
+ // id which identifies single session of user interacting with permission controller
+ optional int64 session_id = 1;
+
+ // UID of package for which permissions are viewed
+ optional int32 uid = 2 [(is_uid) = true];
+
+ // Name of package for which permissions are viewed
+ optional string package_name = 3;
+
+ // The name of a permission group that has been revoked
+ optional string permission_group_name = 4;
+
+ // The age of the app- more than three months old, or more than six months
+ enum Age {
+ UNDEFINED = 0;
+ NEWER_BUCKET = 1;
+ OLDER_BUCKET = 2;
+ }
+
+ // How long the app has been unused. Currently, newer bucket is 3 months, older is 6 months
+ optional Age age = 5;
+}
+
+/**
+* Log that the user has interacted with an app on the auto revoke fragment
+* Logged from ui/handheld/AutoRevokeFragment
+*/
+message AutoRevokedAppInteraction {
+ // id which identifies single session of user interacting with permission controller
+ optional int64 session_id = 1;
+
+ // UID of package for which permissions are viewed
+ optional int32 uid = 2 [(is_uid) = true];
+
+ // Name of package for which permissions are viewed
+ optional string package_name = 3;
+
+ enum Action {
+ UNDEFINED = 0;
+ REMOVE = 1;
+ OPEN = 2;
+ APP_INFO = 3;
+ PERMISSIONS = 4;
+ REMOVE_IN_SETTINGS = 5;
+ OPEN_IN_SETTINGS = 6;
+ }
+
+ // The action the user took to interact with the app
+ optional Action action = 4;
+}
+
+/**
+* Log that the AppPermissionGroupsFragment has been interacted with for the possible purposes of
+* auto revoke, or that the auto revoke switch has been changed
+* Logged from ui/handheld/AppPermissionGroupsFragment
+ */
+message AppPermissionGroupsFragmentAutoRevokeAction {
+ // id which identifies single session of user interacting with permission controller
+ optional int64 session_id = 1;
+
+ // UID of package for which permissions are viewed
+ optional int32 uid = 2 [(is_uid) = true];
+
+ // Name of package for which permissions are viewed
+ optional string package_name = 3;
+
+ enum Action {
+ UNDEFINED = 0;
+ OPENED_FOR_AUTO_REVOKE = 1;
+ OPENED_FROM_INTENT = 2;
+ SWITCH_ENABLED = 3;
+ SWITCH_DISABLED = 4;
+ }
+
+ // The action the user took to interact with the fragment
+ optional Action action = 4;
+}
+
+/**
+ * Logs when there is a smart selection related event.
+ * See frameworks/base/core/java/android/view/textclassifier/TextClassifierEvent.java
+ * Logged from: TextClassifierEventLogger.java
+ */
+message TextSelectionEvent {
+ // A session ID.
+ optional string session_id = 1;
+
+ // Event type of this event.
+ optional android.stats.textclassifier.EventType event_type = 2;
+
+ // Name of the annotator model that is involved in this event.
+ optional string model_name = 3;
+
+ // Type of widget that was involved in triggering this event.
+ optional android.stats.textclassifier.WidgetType widget_type = 4;
+
+ // Index of this event in a session.
+ optional int32 event_index = 5;
+
+ // Entity type that is involved.
+ optional string entity_type = 6;
+
+ // Relative word index of the start of the selection.
+ optional int32 relative_word_start_index = 7;
+
+ // Relative word (exclusive) index of the end of the selection.
+ optional int32 relative_word_end_index = 8;
+
+ // Relative word index of the start of the smart selection.
+ optional int32 relative_suggested_word_start_index = 9;
+
+ // Relative word (exclusive) index of the end of the smart selection.
+ optional int32 relative_suggested_word_end_index = 10;
+
+ // Name of source package.
+ optional string package_name = 11;
+
+ // Name of the LangID model that is involved in this event.
+ optional string langid_model_name = 12;
+}
+
+/**
+ * Logs when there is a smart linkify related event.
+ * See frameworks/base/core/java/android/view/textclassifier/TextClassifierEvent.java
+ * Logged from: TextClassifierEventLogger.java
+ */
+message TextLinkifyEvent {
+ // A session ID.
+ optional string session_id = 1;
+
+ // Event type of this event.
+ optional android.stats.textclassifier.EventType event_type = 2;
+
+ // Name of the annotator model that is involved in this event.
+ optional string model_name = 3;
+
+ // Type of widget that was involved in triggering this event.
+ optional android.stats.textclassifier.WidgetType widget_type = 4;
+
+ // Index of this event in a session.
+ optional int32 event_index = 5;
+
+ // Entity type that is involved.
+ optional string entity_type = 6;
+
+ // Number of links detected.
+ optional int32 num_links = 7;
+
+ // The total length of all links.
+ optional int32 linked_text_length = 8;
+
+ // Length of input text.
+ optional int32 text_length = 9;
+
+ // Time spent on generating links in ms.
+ optional int64 latency_millis = 10;
+
+ // Name of source package.
+ optional string package_name = 11;
+
+ // Name of the LangID model that is involved in this event.
+ optional string langid_model_name = 12;
+}
+
+/**
+ * Logs when there is a conversation actions related event.
+ * See frameworks/base/core/java/android/view/textclassifier/TextClassifierEvent.java
+ * Logged from: TextClassifierEventLogger.java
+ */
+message ConversationActionsEvent {
+ // A session ID.
+ optional string session_id = 1;
+
+ // Event type of this event.
+ optional android.stats.textclassifier.EventType event_type = 2;
+
+ // Name of the actions model that is involved in this event.
+ optional string model_name = 3;
+
+ // Type of widget that was involved in triggering this event.
+ optional android.stats.textclassifier.WidgetType widget_type = 4;
+
+ // The first entity type that is involved.
+ optional string first_entity_type = 5;
+
+ // The second entity type that is involved.
+ optional string second_entity_type = 6;
+
+ // The third entity type that is involved.
+ optional string third_entity_type = 7;
+
+ // The score of the first entity type.
+ optional float score = 8;
+
+ // Name of source package.
+ optional string package_name = 9;
+
+ // Name of the annotator model that is involved in this event.
+ optional string annotator_model_name = 10;
+
+ // Name of the LangID model that is involved in this event.
+ optional string langid_model_name = 11;
+}
+
+/**
+ * Logs when there is a language detection related event.
+ * See frameworks/base/core/java/android/view/textclassifier/TextClassifierEvent.java
+ * Logged from: TextClassifierEventLogger.java
+ */
+message LanguageDetectionEvent {
+ // A session ID.
+ optional string session_id = 1;
+
+ // Event type of this event.
+ optional android.stats.textclassifier.EventType event_type = 2;
+
+ // Name of the language detection model that is involved in this event.
+ optional string model_name = 3;
+
+ // Type of widget that was involved in triggering this event.
+ optional android.stats.textclassifier.WidgetType widget_type = 4;
+
+ // Detected language.
+ optional string language_tag = 5;
+
+ // Score of the detected language.
+ optional float score = 6;
+
+ // Position of this action.
+ optional int32 action_index = 7;
+
+ // Name of source package.
+ optional string package_name = 8;
+}
+
+/**
+ * Information about an OTA update attempt by update_engine.
+ * Logged from platform/system/update_engine/metrics_reporter_android.cc
+ */
+message UpdateEngineUpdateAttemptReported {
+ // The number of attempts for the update engine to apply a given payload.
+ optional int32 attempt_number = 1;
+
+ optional android.stats.otaupdate.PayloadType payload_type = 2;
+
+ // The total time in minutes for the update engine to apply a given payload.
+ // The time is calculated by calling clock_gettime() / CLOCK_BOOTTIME; and
+ // it's increased when the system is sleeping.
+ optional int32 duration_boottime_in_minutes = 3;
+
+ // The total time in minutes for the update engine to apply a given payload.
+ // The time is calculated by calling clock_gettime() / CLOCK_MONOTONIC_RAW;
+ // and it's not increased when the system is sleeping.
+ optional int32 duration_monotonic_in_minutes = 4;
+
+ // The size of the payload in MiBs.
+ optional int32 payload_size_mib = 5;
+
+ // The attempt result reported by the update engine for an OTA update.
+ optional android.stats.otaupdate.AttemptResult attempt_result = 6;
+
+ // The error code reported by the update engine after an OTA update attempt
+ // on A/B devices.
+ optional android.stats.otaupdate.ErrorCode error_code = 7;
+
+ // The build fingerprint of the source system. The value is read from a
+ // system property when the device takes the update. e.g.
+ // Android/aosp_sailfish/sailfish:10/QP1A.190425.004/5507117:userdebug/test-keys
+ optional string source_fingerprint = 8;
+
+ // Size of super partition.
+ optional int64 super_partition_size_bytes = 9;
+
+ // Size of current slot within the super partition.
+ optional int64 slot_size_bytes = 10;
+
+ // Free space available in the super partition.
+ optional int64 super_free_space_bytes = 11;
+}
+
+/**
+ * Information about all the attempts the device make before finishing the
+ * successful update.
+ * Logged from platform/system/update_engine/metrics_reporter_android.cc
+ */
+message UpdateEngineSuccessfulUpdateReported {
+ // The number of attempts for the update engine to apply the payload for a
+ // successful update.
+ optional int32 attempt_count = 1;
+
+ optional android.stats.otaupdate.PayloadType payload_type = 2;
+
+ optional int32 payload_size_mib = 3;
+
+ // The total number of bytes downloaded by update_engine since the last
+ // successful update.
+ optional int32 total_bytes_downloaded_mib = 4;
+
+ // The ratio in percentage of the over-downloaded bytes compared to the
+ // total bytes needed to successfully install the update. e.g. 200 if we
+ // download 200MiB in total for a 100MiB package.
+ optional int32 download_overhead_percentage = 5;
+
+ // The total time in minutes for the update engine to apply the payload for a
+ // successful update.
+ optional int32 total_duration_minutes = 6;
+
+ // The number of reboot of the device during a successful update.
+ optional int32 reboot_count = 7;
+}
+
+/**
+ * Reported when the RebootEscrow HAL has attempted to recover the escrowed
+ * key to indicate whether it was successful or not.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+ */
+message RebootEscrowRecoveryReported {
+ optional bool successful = 1;
+}
+
+/**
+ * Global display pipeline metrics reported by SurfaceFlinger.
+ * Pulled from:
+ * frameworks/native/services/surfaceflinger/TimeStats/TimeStats.cpp
+ */
+message SurfaceflingerStatsGlobalInfo {
+ // Total number of frames presented during the tracing period
+ optional int64 total_frames = 1;
+ // Total number of frames missed
+ optional int64 missed_frames = 2;
+ // Total number of frames that fell back to client composition
+ optional int64 client_composition_frames = 3;
+ // Total time the display was turned on
+ optional int64 display_on_millis = 4;
+ // Total time that was spent performing animations.
+ // This is derived from the present-to-present layer histogram
+ optional int64 animation_millis = 5;
+ // Total number of event connections tracked by SurfaceFlinger at the time
+ // of this pull. If this number grows prohibitively large, then this can
+ // cause jank due to resource contention.
+ optional int32 event_connection_count = 6;
+ // Set of timings measured from when SurfaceFlinger began compositing a
+ // frame, until the frame was requested to be presented to the display. This
+ // measures SurfaceFlinger's total CPU walltime on the critical path per
+ // frame.
+ optional FrameTimingHistogram frame_duration = 7
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+ // Set of timings measured from when SurfaceFlinger first began using the
+ // GPU to composite a frame, until the GPU has finished compositing that
+ // frame. This measures the total additional time SurfaceFlinger needed to
+ // perform due to falling back into GPU composition.
+ optional FrameTimingHistogram render_engine_timing = 8
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Per-layer display pipeline metrics reported by SurfaceFlinger.
+ * The number of layers uploaded will be restricted due to size limitations.
+ * Pulled from:
+ * frameworks/native/services/surfaceflinger/TimeStats/TimeStats.cpp
+ */
+message SurfaceflingerStatsLayerInfo {
+ // The layer for this set of metrics
+ // For now we can infer that the package name is included in the layer
+ // name.
+ optional string layer_name = 1;
+ // Total number of frames presented
+ optional int64 total_frames = 2;
+ // Total number of dropped frames while latching a buffer for this layer.
+ optional int64 dropped_frames = 3;
+ // Set of timings measured between successive presentation timestamps.
+ optional FrameTimingHistogram present_to_present = 4
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+ // Set of timings measured from when an app queued a buffer for
+ // presentation, until the buffer was actually presented to the
+ // display.
+ optional FrameTimingHistogram post_to_present = 5
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+ // Set of timings measured from when a buffer is ready to be presented,
+ // until the buffer was actually presented to the display.
+ optional FrameTimingHistogram acquire_to_present = 6
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+ // Set of timings measured from when a buffer was latched by
+ // SurfaceFlinger, until the buffer was presented to the display
+ optional FrameTimingHistogram latch_to_present = 7
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+ // Set of timings measured from the desired presentation to the actual
+ // presentation time
+ optional FrameTimingHistogram desired_to_present = 8
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+ // Set of timings measured from when an app queued a buffer for
+ // presentation, until the buffer was ready to be presented.
+ optional FrameTimingHistogram post_to_acquire = 9
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+ // Frames missed latch because the acquire fence didn't fire
+ optional int64 late_acquire_frames = 10;
+ // Frames latched early because the desired present time was bad
+ optional int64 bad_desired_present_frames = 11;
+}
+
+/**
+ * Histogram of frame counts bucketed by time in milliseconds.
+ * Because of size limitations, we hard-cap the number of buckets, with
+ * buckets for corresponding to larger milliseconds being less precise.
+ */
+message FrameTimingHistogram {
+ // Timings in milliseconds that describes a set of histogram buckets
+ repeated int32 time_millis_buckets = 1;
+ // Number of frames that match to each time_millis, i.e. the bucket
+ // contents
+ // It's required that len(time_millis) == len(frame_count)
+ repeated int64 frame_counts = 2;
+}
+
+/**
+ * Janky event as reported by SurfaceFlinger.
+ * This event is intended to be consumed by a Perfetto subscriber for
+ * automated trace collection.
+ *
+ * Logged from:
+ * frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
+ */
+message DisplayJankReported {
+ // Informational field for how long the janky event lasted in milliseconds
+ optional int64 event_duration_millis = 1;
+ // Number of frame deadlines missed, where SurfaceFlinger failed to update
+ // the display on time.
+ optional int32 present_deadlines_missed = 2;
+}
+
+/**
+ * Information about camera facing and API level usage.
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/camera/CameraServiceProxy.java
+ */
+message CameraActionEvent {
+ // Camera session duration
+ optional int64 duration_millis = 1;
+
+ // Camera API level used
+ optional int32 api_level = 2;
+
+ // Name of client package
+ optional string package_name = 3;
+
+ // Camera facing
+ enum Facing {
+ UNKNOWN = 0;
+ BACK = 1;
+ FRONT = 2;
+ EXTERNAL = 3;
+ }
+ optional Facing facing = 4;
+}
+
+/**
* Logs when a compatibility change is affecting an app.
*
* Logged from:
@@ -7583,6 +9555,7 @@ message AppCompatibilityChangeReported {
// Where it was logged from.
optional Source source = 4;
+
}
/**
@@ -7649,70 +9622,123 @@ message VmsClientStats {
}
/**
- * Information about an OTA update attempt by update_engine.
- * Logged from platform/system/update_engine/metrics_reporter_android.cc
+ * State of a dangerous permission requested by a package - sampled
+ * Pulled from: StatsCompanionService.java with data obtained from PackageManager API
+*/
+message DangerousPermissionStateSampled {
+ // Name of the permission
+ optional string permission_name = 1;
+
+ // Uid of the package
+ optional int32 uid = 2 [(is_uid) = true];
+
+ // If the permission is granted to the uid
+ optional bool is_granted = 3;
+
+ // Permission flags as per android.content.pm.PermissionFlags
+ optional int32 permission_flags = 4;
+}
+
+/**
+ * HWUI stats for a given app.
*/
-message UpdateEngineUpdateAttemptReported {
- // The number of attempts for the update engine to apply a given payload.
- optional int32 attempt_number = 1;
+message GraphicsStats {
+ // The package name of the app
+ optional string package_name = 1;
- optional android.stats.otaupdate.PayloadType payload_type = 2;
+ // The version code of the app
+ optional int64 version_code = 2;
- // The total time in minutes for the update engine to apply a given payload.
- // The time is calculated by calling clock_gettime() / CLOCK_BOOTTIME; and
- // it's increased when the system is sleeping.
- optional int32 duration_boottime_in_minutes = 3;
+ // The start & end timestamps in UTC as
+ // milliseconds since January 1, 1970
+ // Compatible with java.util.Date#setTime()
+ optional int64 start_millis = 3;
- // The total time in minutes for the update engine to apply a given payload.
- // The time is calculated by calling clock_gettime() / CLOCK_MONOTONIC_RAW;
- // and it's not increased when the system is sleeping.
- optional int32 duration_monotonic_in_minutes = 4;
+ optional int64 end_millis = 4;
- // The size of the payload in MiBs.
- optional int32 payload_size_mib = 5;
+ // HWUI renders pipeline type: GL (1) or Vulkan (2).
+ enum PipelineType {
+ UNKNOWN = 0;
+ GL = 1;
+ VULKAN = 2;
+ }
- // The attempt result reported by the update engine for an OTA update.
- optional android.stats.otaupdate.AttemptResult attempt_result = 6;
+ // HWUI renders pipeline type: GL or Vulkan.
+ optional PipelineType pipeline = 5;
- // The error code reported by the update engine after an OTA update attempt
- // on A/B devices.
- optional android.stats.otaupdate.ErrorCode error_code = 7;
+ // Distinct frame count.
+ optional int32 total_frames = 6;
- // The build fingerprint of the source system. The value is read from a
- // system property when the device takes the update. e.g.
- // Android/aosp_sailfish/sailfish:10/QP1A.190425.004/5507117:userdebug/test-keys
- optional string source_fingerprint = 8;
+ // Number of "missed vsync" events.
+ optional int32 missed_vsync_count = 7;
+
+ // Number of frames in triple-buffering scenario (high input latency)
+ optional int32 high_input_latency_count = 8;
+
+ // Number of "slow UI thread" events.
+ optional int32 slow_ui_thread_count = 9;
+
+ // Number of "slow bitmap upload" events.
+ optional int32 slow_bitmap_upload_count = 10;
+
+ // Number of "slow draw" events.
+ optional int32 slow_draw_count = 11;
+
+ // Number of frames that missed their deadline (aka, visibly janked)
+ optional int32 missed_deadline_count = 12;
+
+ // The frame time histogram for the package
+ optional FrameTimingHistogram cpu_histogram = 13
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+
+ // The gpu frame time histogram for the package
+ optional FrameTimingHistogram gpu_histogram = 14
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+
+ // UI mainline module version.
+ optional int64 version_ui_module = 15;
+
+ // If true, these are HWUI stats for up to a 24h period for a given app from today.
+ // If false, these are HWUI stats for a 24h period for a given app from the last complete
+ // day (yesterday). Stats from yesterday stay constant, while stats from today may change as
+ // more apps are running / rendering.
+ optional bool is_today = 16;
}
/**
- * Information about all the attempts the device make before finishing the
- * successful update.
- * Logged from platform/system/update_engine/metrics_reporter_android.cc
+ * Message related to dangerous (runtime) app ops access
*/
-message UpdateEngineSuccessfulUpdateReported {
- // The number of attempts for the update engine to apply the payload for a
- // successful update.
- optional int32 attempt_count = 1;
+message RuntimeAppOpAccess {
+ // Uid of the package accessing app op
+ optional int32 uid = 1 [(is_uid) = true];
- optional android.stats.otaupdate.PayloadType payload_type = 2;
+ // Name of the package accessing app op
+ optional string package_name = 2;
- optional int32 payload_size_mib = 3;
+ // deprecated - set to empty string
+ optional string op_deprecated = 3 [deprecated = true];
- // The total number of bytes downloaded by update_engine since the last
- // successful update.
- optional int32 total_bytes_downloaded_mib = 4;
+ // attribution_tag; provided by developer when accessing related API, limited at 50 chars by
+ // API. Attributions must be provided through manifest using <attribution> tag available in R
+ // and above.
+ optional string attribution_tag = 4;
- // The ratio in percentage of the over-downloaded bytes compared to the
- // total bytes needed to successfully install the update. e.g. 200 if we
- // download 200MiB in total for a 100MiB package.
- optional int32 download_overhead_percentage = 5;
+ // message related to app op access, limited to 600 chars by API
+ optional string message = 5;
- // The total time in minutes for the update engine to apply the payload for a
- // successful update.
- optional int32 total_duration_minutes = 6;
+ enum SamplingStrategy {
+ DEFAULT = 0;
+ UNIFORM = 1;
+ RARELY_USED = 2;
+ BOOT_TIME_SAMPLING = 3;
+ UNIFORM_OPS = 4;
+ }
- // The number of reboot of the device during a successful update.
- optional int32 reboot_count = 7;
+ // sampling strategy used to collect this message
+ optional SamplingStrategy sampling_strategy = 6;
+
+ // operation id
+ optional android.app.AppOpEnum op = 7 [default = APP_OP_NONE];
}
/*
@@ -7755,6 +9781,274 @@ message UserspaceRebootReported {
optional UserEncryptionState user_encryption_state = 3;
}
+/*
+ * Logs integrity check information during each install.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+ */
+message IntegrityCheckResultReported {
+ optional string package_name = 1;
+ optional string app_certificate_hash = 2;
+ optional int64 version_code = 3;
+ optional string installer_package_name = 4;
+ enum Response {
+ UNKNOWN = 0;
+ ALLOWED = 1;
+ REJECTED = 2;
+ FORCE_ALLOWED = 3;
+ }
+ optional Response response = 5;
+ // An estimate on the cause of the response. This will only be populated for
+ // REJECTED and FORCE_ALLOWED
+ optional bool caused_by_app_cert_rule = 6;
+ optional bool caused_by_installer_rule = 7;
+}
+
+/**
+ * Logs the information about the rules and the provider whenever rules are
+ * pushed into AppIntegrityManager.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+ */
+message IntegrityRulesPushed {
+ optional bool success = 1;
+ // Package name of the app that pushed the rules.
+ optional string rule_provider = 2;
+ // Version string of arbitrary format provided by the rule provider to
+ // identify the rules.
+ optional string rule_version = 3;
+}
+
+/**
+ * Logs when a cell broadcast message is received on the device.
+ *
+ * Logged from Cell Broadcast module and platform:
+ * packages/modules/CellBroadcastService/src/com/android/cellbroadcastservice/
+ * packages/apps/CellBroadcastReceiver/
+ * frameworks/opt/telephony/src/java/com/android/internal/telephony/CellBroadcastServiceManager.java
+ */
+message CellBroadcastMessageReported {
+ // The type of Cell Broadcast message
+ enum CbType {
+ UNKNOWN_TYPE = 0;
+ GSM = 1;
+ CDMA = 2;
+ CDMA_SPC = 3;
+ }
+
+ // The parts of the cell broadcast message pipeline
+ enum ReportSource {
+ UNKNOWN_SOURCE = 0;
+ FRAMEWORK = 1;
+ CB_SERVICE = 2;
+ CB_RECEIVER_APP = 3;
+ }
+
+ // GSM, CDMA, CDMA-SCP
+ optional CbType type = 1;
+
+ // The source of the report
+ optional ReportSource source = 2;
+}
+
+/**
+ * Logs when a cell broadcast message is filtered out, or otherwise intentionally not sent to CBR.
+ *
+ * Logged from CellBroadcastService module:
+ * packages/modules/CellBroadcastService/src/com/android/cellbroadcastservice/
+ */
+message CellBroadcastMessageFiltered {
+ enum FilterReason {
+ NOT_FILTERED = 0;
+ DUPLICATE_MESSAGE = 1;
+ GEOFENCED_MESSAGE = 2;
+ AREA_INFO_MESSAGE = 3;
+ }
+
+ // GSM, CDMA, CDMA-SCP
+ optional CellBroadcastMessageReported.CbType type = 1;
+
+ // The source of the report
+ optional FilterReason filter = 2;
+}
+
+/**
+ * Logs when an error occurs while handling a cell broadcast message;
+ *
+ * Logged from CellBroadcastService module:
+ * packages/modules/CellBroadcastService/src/com/android/cellbroadcastservice/
+ */
+message CellBroadcastMessageError {
+ // The type of error raised when trying to handle a cell broadcast message
+ enum ErrorType {
+ UNKNOWN_TYPE = 0;
+ CDMA_DECODING_ERROR = 1;
+ CDMA_SCP_EMPTY = 2;
+ CDMA_SCP_HANDLING_ERROR = 3;
+ GSM_INVALID_HEADER_LENGTH = 4;
+ GSM_UNSUPPORTED_HEADER_MESSAGE_TYPE = 5;
+ GSM_UNSUPPORTED_HEADER_DATA_CODING_SCHEME = 6;
+ GSM_INVALID_PDU = 7;
+ GSM_INVALID_GEO_FENCING_DATA = 8;
+ GSM_UMTS_INVALID_WAC = 9;
+ FAILED_TO_INSERT_TO_DB = 10;
+ UNEXPECTED_GEOMETRY_FROM_FWK = 11;
+ UNEXPECTED_GSM_MESSAGE_TYPE_FROM_FWK = 12;
+ UNEXPECTED_CDMA_MESSAGE_TYPE_FROM_FWK = 13;
+ UNEXPECTED_CDMA_SCP_MESSAGE_TYPE_FROM_FWK = 14;
+ NO_CONNECTION_TO_CB_SERVICE = 15;
+ }
+
+ // What kind of error occurred
+ optional ErrorType type = 1;
+
+ // Exception message (or log message) associated with the error (max 1000 chars)
+ optional string exception_message = 2;
+}
+
+/**
+ * Logs when a tune occurs through device's Frontend.
+ * This is atom ID 276.
+ *
+ * Logged from:
+ * frameworks/base/media/java/android/media/tv/tuner/Tuner.java
+ */
+message TvTunerStateChanged {
+ enum State {
+ UNKNOWN = 0;
+ TUNING = 1; // Signal is tuned
+ LOCKED = 2; // the signal is locked
+ NOT_LOCKED = 3; // the signal isn’t locked.
+ SIGNAL_LOST = 4; // the signal was locked, but is lost now.
+ SCANNING = 5; // the signal is scanned
+ SCAN_STOPPED = 6; // the scan is stopped.
+ }
+ // The uid of the application that sent this custom atom.
+ optional int32 uid = 1 [(is_uid) = true];
+ // new state
+ optional State state = 2;
+}
+
+/**
+ * Logs the status of a dvr playback or record.
+ * This is atom ID 279.
+ *
+ * Logged from:
+ * frameworks/base/media/java/android/media/tv/tuner/dvr
+ */
+message TvTunerDvrStatus {
+ enum Type {
+ UNKNOWN_TYPE = 0;
+ PLAYBACK = 1; // is a playback
+ RECORD = 2; // is a record
+ }
+ enum State {
+ UNKNOWN_STATE = 0;
+ STARTED = 1; // DVR is started
+ STOPPED = 2; // DVR is stopped
+ }
+ // The uid of the application that sent this custom atom.
+ optional int32 uid = 1 [(is_uid) = true];
+ // DVR type
+ optional Type type = 2;
+ // DVR state
+ optional State state = 3;
+ // Identify the segment of a record or playback
+ optional int32 segment_id = 4;
+ // indicate how many overflow or underflow happened between started to stopped
+ optional int32 overflow_underflow_count = 5;
+}
+
+/**
+ * Logs when a cas session opened through MediaCas.
+ * This is atom ID 280.
+ *
+ * Logged from:
+ * frameworks/base/media/java/android/media/MediaCas.java
+ */
+message TvCasSessionOpenStatus {
+ enum State {
+ UNKNOWN = 0;
+ SUCCEEDED = 1; // indicate that the session is opened successfully.
+ FAILED = 2; // indicate that the session isn’t opened successfully.
+ }
+ // The uid of the application that sent this custom atom.
+ optional int32 uid = 1 [(is_uid) = true];
+ // Cas system Id
+ optional int32 cas_system_id = 2;
+ // State of the session
+ optional State state = 3;
+}
+
+/**
+ * Logs for ContactsProvider general usage.
+ * This is atom ID 301.
+ *
+ * Logged from:
+ * packages/providers/ContactsProvider/src/com/android/providers/contacts/ContactsProvider2.java
+ */
+message ContactsProviderStatusReported {
+ enum ApiType {
+ UNKNOWN_API = 0;
+ QUERY = 1;
+ // INSERT includes insert and bulkInsert, and inserts triggered by applyBatch.
+ INSERT = 2;
+ // UPDATE and DELETE includes update/delete and the ones triggered by applyBatch.
+ UPDATE = 3;
+ DELETE = 4;
+ }
+
+ enum ResultType {
+ UNKNOWN_RESULT = 0;
+ SUCCESS = 1;
+ FAIL = 2;
+ ILLEGAL_ARGUMENT = 3;
+ UNSUPPORTED_OPERATION = 4;
+ }
+
+ enum CallerType {
+ UNSPECIFIED_CALLER_TYPE = 0;
+ CALLER_IS_SYNC_ADAPTER = 1;
+ CALLER_IS_NOT_SYNC_ADAPTER = 2;
+ }
+
+ optional ApiType api_type = 1;
+ // Defined in
+ // packages/providers/ContactsProvider/src/com/android/providers/contacts/ContactsProvider2.java
+ optional int32 uri_type = 2;
+ optional CallerType caller_type = 3;
+ optional ResultType result_type = 4;
+ optional int32 result_count = 5;
+ optional int64 latency_micros = 6;
+}
+
+/**
+ * Logs when an app is frozen or unfrozen.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/CachedAppOptimizer.java
+ */
+message AppFreezeChanged {
+ // The type of event.
+ enum Action {
+ UNKNOWN = 0;
+ FREEZE_APP = 1;
+ UNFREEZE_APP = 2;
+ }
+ optional Action action = 1;
+
+ // Pid of the process being frozen.
+ optional int32 pid = 2;
+
+ // Name of the process being frozen.
+ optional string process_name = 3;
+
+ // Time since last unfrozen.
+ optional int64 time_unfrozen_millis = 4;
+}
+
/**
* Pulls information for a single voice call.
*
@@ -7906,6 +10200,808 @@ message SupportedRadioAccessFamily {
}
/**
+ * Logs gnss stats from location service provider
+ *
+ * Pulled from:
+ * frameworks/base/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
+ */
+
+message GnssStats {
+ // Number of location reports since boot
+ optional int64 location_reports = 1;
+
+ // Total pulled reports of Location failures since boot
+ optional int64 location_failure_reports = 2;
+
+ // Number of time to first fix reports since boot
+ optional int64 time_to_first_fix_reports = 3;
+
+ // Total pulled reported time to first fix (in milli-seconds) since boot
+ optional int64 time_to_first_fix_millis = 4;
+
+ // Number of position accuracy reports since boot
+ optional int64 position_accuracy_reports = 5;
+
+ // Total pulled reported position accuracy (in meters) since boot
+ optional int64 position_accuracy_meters = 6;
+
+ // Number of top 4 average CN0 reports since boot
+ optional int64 top_four_average_cn0_reports = 7;
+
+ // Total pulled reported of top 4 average CN0 (dB-mHz) since boot
+ optional int64 top_four_average_cn0_db_mhz = 8;
+
+ // Number of l5 top 4 average CN0 reports since boot
+ optional int64 l5_top_four_average_cn0_reports = 9;
+
+ // Total pulled reported of l5 top 4 average CN0 (dB-mHz) since boot
+ optional int64 l5_top_four_average_cn0_db_mhz = 10;
+
+ // Total number of sv status messages reports since boot
+ optional int64 sv_status_reports = 11;
+
+ // Total number of sv status messages reports, where sv is used in fix since boot
+ optional int64 sv_status_reports_used_in_fix = 12;
+
+ // Total number of L5 sv status messages reports since boot
+ optional int64 l5_sv_status_reports = 13;
+
+ // Total number of L5 sv status messages reports, where sv is used in fix since boot
+ optional int64 l5_sv_status_reports_used_in_fix = 14;
+}
+
+/**
+ * Logs when an app is moved to a different standby bucket.
+ *
+ * Logged from:
+ * frameworks/base/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+ */
+message AppStandbyBucketChanged {
+ optional string package_name = 1;
+
+ // Should be 0, 10, 11, 12, etc. where 0 is the owner. See UserHandle for more documentation.
+ optional int32 user_id = 2;
+
+ // These enum values match the constants defined in UsageStatsManager.java.
+ enum Bucket {
+ BUCKET_UNKNOWN = 0;
+ BUCKET_EXEMPTED = 5;
+ BUCKET_ACTIVE = 10;
+ BUCKET_WORKING_SET = 20;
+ BUCKET_FREQUENT = 30;
+ BUCKET_RARE = 40;
+ BUCKET_RESTRICTED = 45;
+ BUCKET_NEVER = 50;
+ }
+ optional Bucket bucket = 3;
+
+ enum MainReason {
+ MAIN_UNKNOWN = 0;
+ MAIN_DEFAULT = 0x0100;
+ MAIN_TIMEOUT = 0x0200;
+ MAIN_USAGE = 0x0300;
+ MAIN_FORCED_BY_USER = 0x0400;
+ MAIN_PREDICTED = 0x0500;
+ MAIN_FORCED_BY_SYSTEM = 0x0600;
+ }
+ optional MainReason main_reason = 4;
+
+ // A more detailed reason for the standby bucket change. The sub reason name is dependent on
+ // the main reason. Values are one of the REASON_SUB_XXX constants defined in
+ // UsageStatsManager.java.
+ optional int32 sub_reason = 5;
+}
+
+/**
+* Reports a started sharesheet transaction.
+*
+* Logged from:
+* frameworks/base/core/java/com/android/internal/app/ChooserActivity.java
+*/
+message SharesheetStarted {
+ // The event_id (as for UiEventReported).
+ optional int32 event_id = 1;
+ // The calling app's package name.
+ optional string package_name = 2;
+ // An identifier to tie together multiple logs relating to the same share event
+ optional int32 instance_id = 3;
+ // The mime type of the share
+ optional string mime_type = 4;
+ // The number of direct targets the calling app is providing that will be shown.
+ optional int32 num_app_provided_direct_targets = 5;
+ // The number of app targets the calling app is providing that will be shown.
+ optional int32 num_app_provided_app_targets = 6;
+ // True if the share originates from the workprofile
+ optional bool is_workprofile = 7;
+
+ enum SharesheetPreviewType { // Constants from ChooserActivity.java
+ CONTENT_PREVIEW_TYPE_UNKNOWN = 0; // Default for proto 2 / 3 compatibility.
+ CONTENT_PREVIEW_IMAGE = 1; // The preview shown in the sharesheet is an image.
+ CONTENT_PREVIEW_FILE = 2; // The preview shown in the sharesheet is a file.
+ CONTENT_PREVIEW_TEXT = 3; // The preview shown in the sharesheet is text.
+ }
+ // How the sharesheet preview is presented.
+ optional SharesheetPreviewType preview_type = 8;
+
+ enum ResolverActivityIntent { // Intents handled by ResolverActivity.java
+ INTENT_DEFAULT = 0;
+ INTENT_ACTION_VIEW = 1;
+ INTENT_ACTION_EDIT = 2;
+ INTENT_ACTION_SEND = 3;
+ INTENT_ACTION_SENDTO = 4;
+ INTENT_ACTION_SEND_MULTIPLE = 5;
+ INTENT_ACTION_IMAGE_CAPTURE = 6;
+ INTENT_ACTION_MAIN = 7;
+ }
+ // The intent being processed (only SEND and SEND_MULTIPLE are system sharesheet)
+ optional ResolverActivityIntent intent_type = 9;
+}
+
+/**
+ * Reports a ranking selection event.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/app/ChooserActivity.java (sharesheet)
+ */
+message RankingSelected {
+ // The event_id (as for UiEventReported).
+ optional int32 event_id = 1;
+ // The relevant app's package name (can be source or picked package).
+ optional string package_name = 2;
+ // An identifier to tie together multiple logs relating to the same share event.
+ optional int32 instance_id = 3;
+ // Which of the ranked targets got picked, default starting position 0.
+ optional int32 position_picked = 4;
+}
+
+/**
+ * Logs when TvSettings UI is interacted at.
+ *
+ * Logged from: packages/apps/TvSettings
+ */
+message TvSettingsUIInteracted {
+
+ /** The UI action category */
+ optional android.app.tvsettings.Action action = 1;
+
+ /** The ID of the entry that the users actioned on */
+ optional android.app.tvsettings.ItemId item_id = 2;
+}
+
+/**
+ * Logs information about a package installation using package installer V2 APIs.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
+ */
+message PackageInstallerV2Reported {
+ // Whether this installation uses Incremental File System
+ optional bool is_incremental = 1;
+ // Name of the package that is intended to be installed
+ optional string package_name = 2;
+ // The duration between when the install was requested to when the install has completed
+ optional int64 duration_millis = 3;
+ // Installation result in final integer, which are SystemApi's.
+ // Return_code 1 indicates success.
+ // For full list, see frameworks/base/core/java/android/content/pm/PackageManager.java
+ optional int32 return_code = 4;
+ // Total size of the APKs installed for this package
+ optional int64 apks_size_bytes = 5;
+}
+
+/**
+ * Logs settings provider values.
+ *
+ * Use DeviceConfig.getProperties to get a list Setting key, query the data from content provider,
+ * then write the value to proto.
+ *
+ */
+message SettingSnapshot {
+
+ // Setting key
+ optional string name = 1;
+
+ enum SettingsValueType {
+ NOTASSIGNED = 0;
+ ASSIGNED_BOOL_TYPE = 1;
+ ASSIGNED_INT_TYPE = 2;
+ ASSIGNED_FLOAT_TYPE = 3;
+ ASSIGNED_STRING_TYPE = 4;
+ };
+ // Setting value type
+ optional SettingsValueType type = 2;
+
+ optional bool bool_value = 3;
+
+ optional int32 int_value = 4;
+
+ optional float float_value = 5;
+
+ optional string str_value = 6;
+
+ // Android user index. 0 for primary user, 10, 11 for secondary or profile user
+ optional int32 user_id = 7;
+}
+
+/**
+ * An event logged to indicate that a user journey is about to be performed. This atom includes
+ * relevant information about the users involved in the journey. A UserLifecycleEventOccurred event
+ * will immediately follow this atom which will describe the event(s) and its state.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/UserController.java
+ * frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java
+ */
+message UserLifecycleJourneyReported {
+ // An identifier to track a chain of user lifecycle events occurring (referenced in the
+ // UserLifecycleEventOccurred atom)
+ optional int64 session_id = 1;
+
+ // Indicates what type of user journey this session is related to
+ enum Journey {
+ UNKNOWN = 0; // Undefined user lifecycle journey
+ USER_SWITCH_UI = 1; // A user switch journey where a UI is shown
+ USER_SWITCH_FG = 2; // A user switch journey without a UI shown
+ USER_START = 3; // A user start journey
+ USER_CREATE = 4; // A user creation journey
+ }
+ optional Journey journey = 2;
+ // Which user the journey is originating from - could be -1 for certain phases (eg USER_CREATE)
+ // This integer is a UserIdInt (eg 0 for the system user, 10 for secondary/guest)
+ optional int32 origin_user = 3;
+ // Which user the journey is targeting
+ // This integer is a UserIdInt (eg 0 for the system user, 10 for secondary/guest)
+ optional int32 target_user = 4;
+
+ // What is the user type of the target user
+ // These should be in sync with USER_TYPE_* flags defined in UserManager.java
+ enum UserType {
+ TYPE_UNKNOWN = 0;
+ FULL_SYSTEM = 1;
+ FULL_SECONDARY = 2;
+ FULL_GUEST = 3;
+ FULL_DEMO = 4;
+ FULL_RESTRICTED = 5;
+ PROFILE_MANAGED = 6;
+ SYSTEM_HEADLESS = 7;
+ }
+ optional UserType user_type = 5;
+ // What are the flags attached to the target user
+ optional int32 user_flags = 6;
+}
+
+/**
+ * An event logged when a specific user lifecycle event is performed. These events should be
+ * correlated with a UserLifecycleJourneyReported atom via the session_id.
+ * Note: journeys can span over multiple events, hence some events may share a single session id.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/UserController.java
+ * frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java
+ */
+message UserLifecycleEventOccurred {
+ // An id which links back to user details (reported in the UserLifecycleJourneyReported atom)
+ optional int64 session_id = 1;
+ // The target user for this event (same as target_user in the UserLifecycleJourneyReported atom)
+ // This integer is a UserIdInt (eg 0 for the system user, 10 for secondary/guest)
+ optional int32 user_id = 2;
+
+ enum Event {
+ UNKNOWN = 0; // Indicates that the associated user journey timed-out or resulted in an error
+ SWITCH_USER = 1; // Indicates that this is a user switch event
+ START_USER = 2; // Indicates that this is a user start event
+ CREATE_USER = 3; // Indicates that this is a user create event
+ USER_RUNNING_LOCKED = 4; // Indicates that user is running in locked state
+ UNLOCKING_USER = 5; // Indicates that this is a user unlocking event
+ UNLOCKED_USER = 6; // Indicates that this is a user unlocked event
+ }
+ optional Event event = 3;
+
+ enum State {
+ NONE = 0; // Indicates the associated event has no start/end defined
+ BEGIN = 1;
+ FINISH = 2;
+ }
+ optional State state = 4; // Represents the state of an event (beginning/ending)
+}
+
+/**
+ * Logs when accessibility shortcut clicked.
+ *
+ * Logged from:
+ * frameworks/base/services/accessibility/java/com/android/server/accessibility
+ */
+message AccessibilityShortcutReported {
+ // The accessibility feature(including installed a11y service, framework a11y feature,
+ // and installed a11y activity) package name that is assigned to the accessibility shortcut.
+ optional string package_name = 1;
+
+ // The definition of the accessibility shortcut.
+ // From frameworks/base/core/proto/android/stats/accessibility/accessibility_enums.proto.
+ optional android.stats.accessibility.ShortcutType shortcut_type = 2;
+
+ // The definition of the service status.
+ // From frameworks/base/core/proto/android/stats/accessibility/accessibility_enums.proto.
+ optional android.stats.accessibility.ServiceStatus service_status = 3;
+}
+
+/**
+ * Logs when accessibility service status changed.
+ *
+ * Logged from:
+ * packages/apps/Settings/src/com/android/settings/accessibility
+ */
+message AccessibilityServiceReported {
+ // The accessibility service package name.
+ optional string package_name = 1;
+
+ // The definition of the service status.
+ // From frameworks/base/core/proto/android/stats/accessibility/accessibility_enums.proto.
+ optional android.stats.accessibility.ServiceStatus service_status = 2;
+}
+
+/**
+ * Logs when display wake up.
+ *
+ * Logged from:
+ * services/core/java/com/android/server/power/Notifier.java
+ */
+
+message DisplayWakeReported {
+ // Wake_up_reason code
+ // If LOWORD(wake_up_reason) = 0
+ // reference to HIWORD(wake_up_reason) PowerManager.WAKE_REASON_XXX
+ // else reference wake_up_reason to
+ // services/core/java/com/android/server/power/Notifier.java#onWakeUp
+ optional int32 wake_up_reason = 1;
+}
+
+/**
+ * Logs app usage events.
+ */
+message AppUsageEventOccurred {
+ optional int32 uid = 1 [(is_uid) = true];
+ optional string package_name = 2;
+ optional string class_name = 3;
+
+ enum EventType {
+ NONE = 0;
+ MOVE_TO_FOREGROUND = 1;
+ MOVE_TO_BACKGROUND = 2;
+ }
+ optional EventType event_type = 4;
+}
+
+/*
+ * Quality metrics logged when EVS cameras are active.
+ *
+ * Logged from:
+ * packages/services/Car/evs/manager/1.1/Enumerator.cpp
+ */
+message EvsUsageStatsReported {
+
+ // Camera identifier to distinguish the source camera device. This is not
+ // globally unique and therefore cannot be used to identify the user and/or
+ // the device.
+ optional int32 device_id = 1;
+
+ // Peak number of clients during the service
+ optional int32 peak_num_clients = 2;
+
+ // Number of erroneous events during the service
+ optional int32 num_errors = 3;
+
+ // Round trip latency of the very first frame
+ optional int64 first_latency_millis = 4;
+
+ // Average frame round trip latency
+ optional float avg_latency_millis = 5;
+
+ // Peak frame round trip latency
+ optional int64 peak_latency_millis = 6;
+
+ // Total number of frames received
+ optional int64 total_frames = 7;
+
+ // Number of frames ignored
+ optional int64 ignored_frames = 8;
+
+ // Number of dropped frames to synchronize camera devices
+ optional int64 dropped_frames_to_sync = 9;
+
+ // The duration of the service
+ optional int64 duration_millis = 10;
+}
+
+/**
+ * Logs audio power usage stats.
+ *
+ * Pushed from:
+ * frameworks/av/services/mediametrics/AudioPowerUsage.cpp
+ */
+message AudioPowerUsageDataReported {
+ /**
+ * Device used for input/output
+ *
+ * All audio devices please refer to below file:
+ * system/media/audio/include/system/audio-base.h
+ *
+ * Define our own enum values because we don't report all audio devices.
+ * Currently, we only report built-in audio devices such as handset, speaker,
+ * built-in mics, common audio devices such as wired headset, usb headset
+ * and bluetooth devices.
+ */
+ enum AudioDevice {
+ OUTPUT_EARPIECE = 0x1; // handset
+ OUTPUT_SPEAKER = 0x2; // dual speaker
+ OUTPUT_WIRED_HEADSET = 0x4; // 3.5mm headset
+ OUTPUT_USB_HEADSET = 0x8; // usb headset
+ OUTPUT_BLUETOOTH_SCO = 0x10; // bluetooth sco
+ OUTPUT_BLUETOOTH_A2DP = 0x20; // a2dp
+ OUTPUT_SPEAKER_SAFE = 0x40; // bottom speaker
+
+ INPUT_DEVICE_BIT = 0x40000000; // non-negative positive int32.
+ INPUT_BUILTIN_MIC = 0x40000001; // buildin mic
+ INPUT_BUILTIN_BACK_MIC = 0x40000002; // buildin back mic
+ INPUT_WIRED_HEADSET_MIC = 0x40000004; // 3.5mm headset mic
+ INPUT_USB_HEADSET_MIC = 0x40000008; // usb headset mic
+ INPUT_BLUETOOTH_SCO = 0x40000010; // bluetooth sco mic
+ }
+ optional AudioDevice audio_device = 1;
+
+ // Duration of the audio in seconds
+ optional int32 duration_secs = 2;
+
+ // Average volume (0 ... 1.0)
+ optional float average_volume = 3;
+
+ enum AudioType {
+ UNKNOWN_TYPE = 0;
+ VOICE_CALL_TYPE = 1; // voice call
+ VOIP_CALL_TYPE = 2; // voip call, including uplink and downlink
+ MEDIA_TYPE = 3; // music and system sound
+ RINGTONE_NOTIFICATION_TYPE = 4; // ringtone and notification
+ ALARM_TYPE = 5; // alarm type
+ // record type
+ CAMCORDER_TYPE = 6; // camcorder
+ RECORD_TYPE = 7; // other recording
+ }
+ optional AudioType type = 4;
+}
+
+/**
+ * Pulls bytes transferred over WiFi and mobile networks sliced by uid, is_metered, and tag.
+ *
+ * Pulled from:
+ * StatsPullAtomService, which uses NetworkStatsService to query NetworkStats.
+ */
+message BytesTransferByTagAndMetered {
+ optional int32 uid = 1 [(is_uid) = true];
+
+ optional bool is_metered = 2;
+
+ optional int32 tag = 3;
+
+ optional int64 rx_bytes = 4;
+
+ optional int64 rx_packets = 5;
+
+ optional int64 tx_bytes = 6;
+
+ optional int64 tx_packets = 7;
+}
+
+/*
+ * Logs when the Media Output Switcher finishes a media switch operation.
+ *
+ * Logged from:
+ * packages/apps/Settings/src/com/android/settings/media/MediaOutputSliceWorker.java
+ */
+message MediaOutputOpSwitchReported {
+ // Source medium type before switching.
+ optional android.app.settings.mediaoutput.MediumType source = 1;
+
+ // Target medium type after switching.
+ optional android.app.settings.mediaoutput.MediumType target = 2;
+
+ // The result of switching.
+ optional android.app.settings.mediaoutput.SwitchResult result = 3;
+
+ // The detail code of a switching result.
+ optional android.app.settings.mediaoutput.SubResult subresult = 4;
+
+ /*
+ * The package name of a pre-installed app, whose media session is being switched.
+ */
+ optional string media_session_package_name = 5;
+
+ // The amount of available wired devices when a switching is being performed.
+ optional int32 available_wired_device_count = 6;
+
+ // The amount of available Bluetooth devices a switching is being performed.
+ optional int32 available_bt_device_count = 7;
+
+ // The amount of available remote devices when a switching is being performed.
+ optional int32 available_remote_device_count = 8;
+
+ // The amount of applied devices within a remote dynamic group after a switching is done.
+ optional int32 applied_device_count_within_remote_group = 9;
+}
+
+/**
+ * Logs when the Assistant is invoked.
+ *
+ * Logged from:
+ * frameworks/base/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+ */
+message AssistantInvocationReported {
+
+ // The event_id (as for UiEventReported).
+ optional int32 event_id = 1;
+
+ // The registered Assistant's uid and package (as for UiEventReported).
+ optional int32 uid = 2 [(is_uid) = true];
+ optional string package_name = 3;
+
+ // An identifier used to disambiguate which logs refer to a particular invocation of the
+ // Assistant (as for UiEventReported).
+ optional int32 instance_id = 4;
+
+ // The state of the device at the time of invocation.
+ enum DeviceState {
+ UNKNOWN_DEVICE_STATE = 0;
+ AOD1 = 1;
+ AOD2 = 2;
+ BOUNCER = 3;
+ UNLOCKED_LOCKSCREEN = 4;
+ LAUNCHER_HOME = 5;
+ LAUNCHER_OVERVIEW = 6;
+ LAUNCHER_ALL_APPS = 7;
+ APP_DEFAULT = 8;
+ APP_IMMERSIVE = 9;
+ APP_FULLSCREEN = 10;
+ }
+ optional DeviceState device_state = 5;
+
+ // Whether the Assistant handles were showing at the time of invocation.
+ optional bool assistant_handles_showing = 6;
+}
+
+/**
+ * Logs when an AudioRecord finishes running on an audio device
+ *
+ * Logged from:
+ * frameworks/av/services/mediametrics/AudioAnalytics.cpp
+ */
+message MediametricsAudioRecordDeviceUsageReported {
+ // The devices connected to this AudioRecord.
+ // A string OR of various input device categories, e.g. "DEVICE1|DEVICE2".
+ // See lookup<INPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp
+ // See audio_device_t in system/media/audio/include/system/audio-base.h
+ optional string devices = 1;
+
+ // The name of the remote device attached to the device, typically available for USB or BT.
+ // This may be empty for a fixed device, or separated by "|" if more than one.
+ optional string device_names = 2;
+
+ // The amount of time spent in the device as measured by the active track in AudioFlinger.
+ optional int64 device_time_nanos = 3;
+
+ // The audio data format used for encoding.
+ // An enumeration from system/media/audio/include/system/audio-base.h audio_format_t
+ optional string encoding = 4;
+
+ // The client-server buffer framecount.
+ // The framecount is generally between 960 - 48000 for PCM encoding.
+ // The framecount represents raw buffer size in bytes for non-PCM encoding.
+ optional int32 frame_count = 5;
+
+ // The number of audio intervals (contiguous, continuous playbacks).
+ optional int32 interval_count = 6;
+
+ // The sample rate of the AudioRecord.
+ // A number generally between 8000-96000 (frames per second).
+ optional int32 sample_rate = 7;
+
+ // The audio input flags used to construct the AudioRecord.
+ // A string OR from system/media/audio/include/system/audio-base.h audio_input_flags_t
+ optional string flags = 8;
+
+ // The santized package name of the audio client associated with the AudioRecord.
+ // See getSanitizedPackageNameAndVersionCode() in
+ // frameworks/av/services/mediametrics/MediaMetricsService.cpp
+ optional string package_name = 9;
+
+ // The selected device id (nonzero if a non-default device is selected)
+ optional int32 selected_device_id = 10;
+
+ // The caller of the AudioRecord.
+ // See lookup<CALLER_NAME>() in frameworks/av/services/mediametrics/AudioTypes.cpp
+ optional string caller = 11;
+
+ // The audio source for AudioRecord.
+ // An enumeration from system/media/audio/include/system/audio-base.h audio_source_t
+ optional string source = 12;
+}
+
+/**
+ * Logs when an AudioThread finishes running on an audio device
+ *
+ * Logged from:
+ * frameworks/av/services/mediametrics/AudioAnalytics.cpp
+ */
+message MediametricsAudioThreadDeviceUsageReported {
+ // The devices connected to this audio thread.
+ // A string OR of various input device categories, e.g. "DEVICE1|DEVICE2".
+ // (for record threads):
+ // See lookup<INPUT_DEVICE> in frameworks/av/services/mediametrics/AudioTypes.cpp
+ // (for playback threads):
+ // See lookup<OUTPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp
+ // See audio_device_t in system/media/audio/include/system/audio-base.h
+ optional string devices = 1;
+
+ // The name of the remote device attached to the device, typically available for USB or BT.
+ // This may be empty for a fixed device, or separated by "|" if more than one.
+ optional string device_names = 2;
+
+ // The amount of time spent in the device as measured by the active track in AudioFlinger.
+ optional int64 device_time_nanos = 3;
+
+ // The audio data format used for encoding.
+ // An enumeration from system/media/audio/include/system/audio-base.h audio_format_t
+ optional string encoding = 4;
+
+ // The framecount of the buffer delivered to (or from) the HAL.
+ // The framecount is generally ~960 for PCM encoding.
+ // The framecount represents raw buffer size in bytes for non-PCM encoding.
+ optional int32 frame_count = 5;
+
+ // The number of audio intervals (contiguous, continuous playbacks).
+ optional int32 interval_count = 6;
+
+ // The sample rate of the audio thread.
+ // A number generally between 8000-96000 (frames per second).
+ optional int32 sample_rate = 7;
+
+ // The audio flags used to construct the thread
+ // (for record threads):
+ // A string OR from system/media/audio/include/system/audio-base.h audio_input_flags_t
+ // (for playback threads):
+ // A string OR from system/media/audio/include/system/audio-base.h audio_output_flags_t
+ optional string flags = 8;
+
+ // The number of underruns encountered for a playback thread or the
+ // number of overruns encountered for a capture thread.
+ optional int32 xruns = 9;
+
+ // The type of thread
+ // A thread type enumeration from
+ // frameworks/av/mediametrics/services/Translate.h
+ optional string type = 10;
+}
+
+/**
+ * Logs when an AudioTrack finishes running on an audio device
+ *
+ * Logged from:
+ * frameworks/av/services/mediametrics/AudioAnalytics.cpp
+ */
+message MediametricsAudioTrackDeviceUsageReported {
+ // The output devices connected to this AudioTrack.
+ // A string OR of various output device categories, e.g. "DEVICE1|DEVICE2".
+ // See lookup<OUTPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp
+ // See audio_device_t in system/media/audio/include/system/audio-base.h
+ optional string devices = 1;
+
+ // The name of the remote device attached to the device, typically available for USB or BT.
+ // This may be empty for a fixed device, or separated by "|" if more than one.
+ optional string device_names = 2;
+
+ // The amount of time spent in the device as measured by the active track in AudioFlinger.
+ optional int64 device_time_nanos = 3;
+
+ // The audio data format used for encoding.
+ // An enumeration from system/media/audio/include/system/audio-base.h audio_format_t
+ optional string encoding = 4;
+
+ // The client-server buffer framecount.
+ // The framecount is generally between 960 - 48000 for PCM encoding.
+ // The framecount represents raw buffer size in bytes for non-PCM encoding.
+ // A static track (see traits) may have a very large framecount.
+ optional int32 frame_count = 5;
+
+ // The number of audio intervals (contiguous, continuous playbacks).
+ optional int32 interval_count = 6;
+
+ // The sample rate of the AudioTrack.
+ // A number generally between 8000-96000 (frames per second).
+ optional int32 sample_rate = 7;
+
+ // The audio flags used to construct the AudioTrack.
+ // A string OR from system/media/audio/include/system/audio-base.h audio_output_flags_t
+ optional string flags = 8;
+
+ // The number of underruns encountered.
+ optional int32 xruns = 9;
+
+ // The santized package name of the audio client associated with the AudioTrack.
+ // See getSanitizedPackageNameAndVersionCode() in
+ // frameworks/av/services/mediametrics/MediaMetricsService.cpp
+ optional string package_name = 10;
+
+ // The latency of the last sample in the buffer in milliseconds.
+ optional float device_latency_millis = 11;
+
+ // The startup time in milliseconds from start() to sample played.
+ optional float device_startup_millis = 12;
+
+ // The average volume of the track on the device [ 0.f - 1.f ]
+ optional float device_volume = 13;
+
+ // The selected device id (nonzero if a non-default device is selected)
+ optional int32 selected_device_id = 14;
+
+ // The stream_type category for the AudioTrack.
+ // An enumeration from system/media/audio/include/system/audio-base.h audio_stream_type_t
+ optional string stream_type = 15;
+
+ // The usage for the AudioTrack.
+ // An enumeration from system/media/audio/include/system/audio-base.h audio_usage_t
+ optional string usage = 16;
+
+ // The content type of the AudioTrack.
+ // An enumeration from system/media/audio/include/system/audio-base.h audio_content_type_t
+ optional string content_type = 17;
+
+ // The caller of the AudioTrack.
+ // See lookup<CALLER_NAME>() in frameworks/av/services/mediametrics/AudioTypes.cpp
+ optional string caller = 18;
+
+ // The traits of the AudioTrack.
+ // A string OR of different traits, may be empty string.
+ // Only "static" is supported for R.
+ // See lookup<TRACK_TRAITS>() in frameworks/av/services/mediametrics/AudioTypes.cpp
+ optional string traits = 19;
+}
+
+/**
+ * Logs the status of an audio device connection attempt.
+ *
+ * Logged from:
+ * frameworks/av/services/mediametrics/AudioAnalytics.cpp
+ */
+message MediametricsAudioDeviceConnectionReported {
+ // The input devices represented by this report.
+ // A string OR of various input device categories, e.g. "DEVICE1|DEVICE2".
+ // See lookup<INPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp
+ // See audio_device_t in system/media/audio/include/system/audio-base.h
+ optional string input_devices = 1;
+
+ // The output devices represented by this report.
+ // A string OR of various output device categories.
+ // See lookup<OUTPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp
+ // See audio_device_t in system/media/audio/include/system/audio-base.h
+ optional string output_devices = 2;
+
+ // The name of the remote device attached to the device, typically available for USB or BT.
+ // This may be empty for a fixed device, or separated by "|" if more than one.
+ optional string device_names = 3;
+
+ // The result of the audio device connection.
+ // 0 indicates success: connection verified.
+ // 1 indicates unknown: connection not verified or not known if diverted properly.
+ // Other values indicate specific status.
+ // See DeviceConnectionResult in frameworks/av/services/mediametrics/AudioTypes.h
+ optional int32 result = 4;
+
+ // Average milliseconds of time to connect
+ optional float time_to_connect_millis = 5;
+
+ // Number of connections if aggregated statistics, otherwise 1.
+ optional int32 connection_count = 6;
+}
+
+/**
* Logs: i) creation of different types of cryptographic keys in the keystore,
* ii) operations performed using the keys,
* iii) attestation of the keys
@@ -7999,9 +11095,9 @@ message KeystoreKeyEventReported {
optional KeyBlobUsageRequirements key_blob_usage_reqs = 11;
enum Type {
- KEY_OPERATION = 0;
- KEY_CREATION = 1;
- KEY_ATTESTATION = 2;
+ key_operation = 0;
+ key_creation = 1;
+ key_attestation = 2;
}
/** Key creation event, operation event or attestation event? */
optional Type type = 12;
@@ -8012,3 +11108,72 @@ message KeystoreKeyEventReported {
/** Response code or error code */
optional int32 error_code = 14;
}
+
+// Blob Committer stats
+// Keep in sync between:
+// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto
+// frameworks/base/cmds/statsd/src/atoms.proto
+message BlobCommitterProto {
+ // Committer app's uid
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // Unix epoch timestamp of the commit in milliseconds
+ optional int64 commit_timestamp_millis = 2;
+
+ // Flags of what access types the committer has set for the Blob
+ optional int32 access_mode = 3;
+
+ // Number of packages that have been whitelisted for ACCESS_TYPE_WHITELIST
+ optional int32 num_whitelisted_package = 4;
+}
+
+// Blob Leasee stats
+// Keep in sync between:
+// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto
+// frameworks/base/cmds/statsd/src/atoms.proto
+message BlobLeaseeProto {
+ // Leasee app's uid
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // Unix epoch timestamp for lease expiration in milliseconds
+ optional int64 lease_expiry_timestamp_millis = 2;
+}
+
+// List of Blob Committers
+// Keep in sync between:
+// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto
+// frameworks/base/cmds/statsd/src/atoms.proto
+message BlobCommitterListProto {
+ repeated BlobCommitterProto committer = 1;
+}
+
+// List of Blob Leasees
+// Keep in sync between:
+// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto
+// frameworks/base/cmds/statsd/src/atoms.proto
+message BlobLeaseeListProto {
+ repeated BlobLeaseeProto leasee = 1;
+}
+
+/**
+ * Logs the current state of a Blob committed with BlobStoreManager
+ *
+ * Pulled from:
+ * frameworks/base/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+ */
+message BlobInfo {
+ // Id of the Blob
+ optional int64 blob_id = 1;
+
+ // Size of the Blob data
+ optional int64 size = 2;
+
+ // Unix epoch timestamp of the Blob's expiration in milliseconds
+ optional int64 expiry_timestamp_millis = 3;
+
+ // List of committers of this Blob
+ optional BlobCommitterListProto committers = 4;
+
+ // List of leasees of this Blob
+ optional BlobLeaseeListProto leasees = 5;
+}
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
index cf0cc3d59d86..e9875baf58c7 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -37,7 +37,8 @@ CombinationConditionTracker::~CombinationConditionTracker() {
bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConfig,
const vector<sp<ConditionTracker>>& allConditionTrackers,
const unordered_map<int64_t, int>& conditionIdIndexMap,
- vector<bool>& stack) {
+ vector<bool>& stack,
+ vector<ConditionState>& initialConditionCache) {
VLOG("Combination predicate init() %lld", (long long)mConditionId);
if (mInitialized) {
return true;
@@ -73,15 +74,15 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf
return false;
}
-
- bool initChildSucceeded = childTracker->init(allConditionConfig, allConditionTrackers,
- conditionIdIndexMap, stack);
+ bool initChildSucceeded =
+ childTracker->init(allConditionConfig, allConditionTrackers, conditionIdIndexMap,
+ stack, initialConditionCache);
if (!initChildSucceeded) {
ALOGW("Child initialization failed %lld ", (long long)child);
return false;
} else {
- ALOGW("Child initialization success %lld ", (long long)child);
+ VLOG("Child initialization success %lld ", (long long)child);
}
if (allConditionTrackers[childIndex]->isSliced()) {
@@ -95,6 +96,11 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf
childTracker->getLogTrackerIndex().end());
}
+ mUnSlicedPartCondition = evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation,
+ initialConditionCache);
+ initialConditionCache[mIndex] =
+ evaluateCombinationCondition(mChildren, mLogicalOperation, initialConditionCache);
+
// unmark this node in the recursion stack.
stack[mIndex] = false;
@@ -105,20 +111,14 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf
void CombinationConditionTracker::isConditionMet(
const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
- const std::vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
const bool isPartialLink,
- vector<ConditionState>& conditionCache,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
+ vector<ConditionState>& conditionCache) const {
// So far, this is fine as there is at most one child having sliced output.
for (const int childIndex : mChildren) {
if (conditionCache[childIndex] == ConditionState::kNotEvaluated) {
allConditions[childIndex]->isConditionMet(conditionParameters, allConditions,
- dimensionFields,
- isSubOutputDimensionFields,
isPartialLink,
- conditionCache,
- dimensionsKeySet);
+ conditionCache);
}
}
conditionCache[mIndex] =
@@ -147,17 +147,14 @@ void CombinationConditionTracker::evaluateCondition(
ConditionState newCondition =
evaluateCombinationCondition(mChildren, mLogicalOperation, nonSlicedConditionCache);
if (!mSliced) {
+ bool nonSlicedChanged = (mUnSlicedPartCondition != newCondition);
+ mUnSlicedPartCondition = newCondition;
- bool nonSlicedChanged = (mNonSlicedConditionState != newCondition);
- mNonSlicedConditionState = newCondition;
-
- nonSlicedConditionCache[mIndex] = mNonSlicedConditionState;
-
+ nonSlicedConditionCache[mIndex] = mUnSlicedPartCondition;
conditionChangedCache[mIndex] = nonSlicedChanged;
- mUnSlicedPart = newCondition;
} else {
- mUnSlicedPart = evaluateCombinationCondition(
- mUnSlicedChildren, mLogicalOperation, nonSlicedConditionCache);
+ mUnSlicedPartCondition = evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation,
+ nonSlicedConditionCache);
for (const int childIndex : mChildren) {
// If any of the sliced condition in children condition changes, the combination
@@ -173,25 +170,6 @@ void CombinationConditionTracker::evaluateCondition(
}
}
-ConditionState CombinationConditionTracker::getMetConditionDimension(
- const std::vector<sp<ConditionTracker>>& allConditions,
- const std::vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
- vector<ConditionState> conditionCache(allConditions.size(), ConditionState::kNotEvaluated);
- // So far, this is fine as there is at most one child having sliced output.
- for (const int childIndex : mChildren) {
- conditionCache[childIndex] = conditionCache[childIndex] |
- allConditions[childIndex]->getMetConditionDimension(
- allConditions, dimensionFields, isSubOutputDimensionFields, dimensionsKeySet);
- }
- evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
- if (conditionCache[mIndex] == ConditionState::kTrue && dimensionsKeySet.empty()) {
- dimensionsKeySet.insert(DEFAULT_DIMENSION_KEY);
- }
- return conditionCache[mIndex];
-}
-
bool CombinationConditionTracker::equalOutputDimensions(
const std::vector<sp<ConditionTracker>>& allConditions,
const vector<Matcher>& dimensions) const {
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
index 481cb200d8e6..39ff0ab03266 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.h
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -32,8 +32,8 @@ public:
bool init(const std::vector<Predicate>& allConditionConfig,
const std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& conditionIdIndexMap,
- std::vector<bool>& stack) override;
+ const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack,
+ std::vector<ConditionState>& initialConditionCache) override;
void evaluateCondition(const LogEvent& event,
const std::vector<MatchingState>& eventMatcherValues,
@@ -43,17 +43,8 @@ public:
void isConditionMet(const ConditionKey& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
const bool isPartialLink,
- std::vector<ConditionState>& conditionCache,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
-
- ConditionState getMetConditionDimension(
- const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
+ std::vector<ConditionState>& conditionCache) const override;
// Only one child predicate can have dimension.
const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
index 5ff0e1d5a885..62736c8160bb 100644
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -36,7 +36,7 @@ public:
mIndex(index),
mInitialized(false),
mTrackerIndex(),
- mNonSlicedConditionState(ConditionState::kUnknown),
+ mUnSlicedPartCondition(ConditionState::kUnknown),
mSliced(false){};
virtual ~ConditionTracker(){};
@@ -51,10 +51,12 @@ public:
// need to call init() on children conditions)
// conditionIdIndexMap: the mapping from condition id to its index.
// stack: a bit map to keep track which nodes have been visited on the stack in the recursion.
+ // initialConditionCache: tracks initial conditions of all ConditionTrackers.
virtual bool init(const std::vector<Predicate>& allConditionConfig,
const std::vector<sp<ConditionTracker>>& allConditionTrackers,
const std::unordered_map<int64_t, int>& conditionIdIndexMap,
- std::vector<bool>& stack) = 0;
+ std::vector<bool>& stack,
+ std::vector<ConditionState>& initialConditionCache) = 0;
// evaluate current condition given the new event.
// event: the new log event
@@ -72,39 +74,19 @@ public:
std::vector<ConditionState>& conditionCache,
std::vector<bool>& conditionChanged) = 0;
- // Return the current condition state.
- virtual ConditionState isConditionMet() const {
- return mNonSlicedConditionState;
- };
-
// Query the condition with parameters.
// [conditionParameters]: a map from condition name to the HashableDimensionKey to query the
// condition.
// [allConditions]: all condition trackers. This is needed because the condition evaluation is
// done recursively
- // [dimensionFields]: the needed dimension fields which should be all or subset of the condition
- // tracker output dimension.
- // [isSubOutputDimensionFields]: true if the needed dimension fields which is strictly subset of
- // the condition tracker output dimension.
// [isPartialLink]: true if the link specified by 'conditionParameters' contains all the fields
// in the condition tracker output dimension.
// [conditionCache]: the cache holding the condition evaluation values.
- // [dimensionsKeySet]: the dimensions where the sliced condition is true. For combination
- // condition, it assumes that only one child predicate is sliced.
virtual void isConditionMet(
const ConditionKey& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
const bool isPartialLink,
- std::vector<ConditionState>& conditionCache,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const = 0;
-
- virtual ConditionState getMetConditionDimension(
- const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const = 0;
+ std::vector<ConditionState>& conditionCache) const = 0;
// return the list of LogMatchingTracker index that this ConditionTracker uses.
virtual const std::set<int>& getLogTrackerIndex() const {
@@ -140,8 +122,9 @@ public:
const std::vector<sp<ConditionTracker>>& allConditions,
const vector<Matcher>& dimensions) const = 0;
+ // Return the current condition state of the unsliced part of the condition.
inline ConditionState getUnSlicedPartConditionState() const {
- return mUnSlicedPart;
+ return mUnSlicedPartCondition;
}
protected:
@@ -156,10 +139,16 @@ protected:
// the list of LogMatchingTracker index that this ConditionTracker uses.
std::set<int> mTrackerIndex;
- ConditionState mNonSlicedConditionState;
+ // This variable is only used for CombinationConditionTrackers.
+ // SimpleConditionTrackers technically don't have an unsliced part because
+ // they are either sliced or unsliced.
+ //
+ // CombinationConditionTrackers have multiple children ConditionTrackers
+ // that can be a mixture of sliced or unsliced. This tracks the
+ // condition of the unsliced part of the combination condition.
+ ConditionState mUnSlicedPartCondition;
bool mSliced;
- ConditionState mUnSlicedPart;
};
} // namespace statsd
diff --git a/cmds/statsd/src/condition/ConditionWizard.cpp b/cmds/statsd/src/condition/ConditionWizard.cpp
index f371316a1420..c542032b48ea 100644
--- a/cmds/statsd/src/condition/ConditionWizard.cpp
+++ b/cmds/statsd/src/condition/ConditionWizard.cpp
@@ -22,27 +22,15 @@ namespace statsd {
using std::vector;
ConditionState ConditionWizard::query(const int index, const ConditionKey& parameters,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- const bool isPartialLink,
- std::unordered_set<HashableDimensionKey>* dimensionKeySet) {
+ const bool isPartialLink) {
vector<ConditionState> cache(mAllConditions.size(), ConditionState::kNotEvaluated);
mAllConditions[index]->isConditionMet(
- parameters, mAllConditions, dimensionFields, isSubOutputDimensionFields, isPartialLink,
- cache, *dimensionKeySet);
+ parameters, mAllConditions, isPartialLink,
+ cache);
return cache[index];
}
-ConditionState ConditionWizard::getMetConditionDimension(
- const int index, const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- std::unordered_set<HashableDimensionKey>* dimensionsKeySet) const {
- return mAllConditions[index]->getMetConditionDimension(mAllConditions, dimensionFields,
- isSubOutputDimensionFields,
- *dimensionsKeySet);
-}
-
const set<HashableDimensionKey>* ConditionWizard::getChangedToTrueDimensions(
const int index) const {
return mAllConditions[index]->getChangedToTrueDimensions(mAllConditions);
@@ -79,4 +67,4 @@ bool ConditionWizard::equalOutputDimensions(const int index, const vector<Matche
} // namespace statsd
} // namespace os
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h
index 2c8814772839..892647910d9f 100644
--- a/cmds/statsd/src/condition/ConditionWizard.h
+++ b/cmds/statsd/src/condition/ConditionWizard.h
@@ -40,15 +40,7 @@ public:
// The ConditionTracker at [conditionIndex] can be a CombinationConditionTracker. In this case,
// the conditionParameters contains the parameters for it's children SimpleConditionTrackers.
virtual ConditionState query(const int conditionIndex, const ConditionKey& conditionParameters,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- const bool isPartialLink,
- std::unordered_set<HashableDimensionKey>* dimensionKeySet);
-
- virtual ConditionState getMetConditionDimension(
- const int index, const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- std::unordered_set<HashableDimensionKey>* dimensionsKeySet) const;
+ const bool isPartialLink);
virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(const int index) const;
virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index 12ff184a2608..efb4d4989425 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -85,12 +85,6 @@ SimpleConditionTracker::SimpleConditionTracker(
mInitialValue = ConditionState::kUnknown;
}
- mNonSlicedConditionState = mInitialValue;
-
- if (!mSliced) {
- mUnSlicedPart = mInitialValue;
- }
-
mInitialized = true;
}
@@ -101,9 +95,11 @@ SimpleConditionTracker::~SimpleConditionTracker() {
bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig,
const vector<sp<ConditionTracker>>& allConditionTrackers,
const unordered_map<int64_t, int>& conditionIdIndexMap,
- vector<bool>& stack) {
+ vector<bool>& stack,
+ vector<ConditionState>& initialConditionCache) {
// SimpleConditionTracker does not have dependency on other conditions, thus we just return
// if the initialization was successful.
+ initialConditionCache[mIndex] = mInitialValue;
return mInitialized;
}
@@ -141,9 +137,6 @@ void SimpleConditionTracker::handleStopAll(std::vector<ConditionState>& conditio
mInitialValue = ConditionState::kFalse;
mSlicedConditionState.clear();
conditionCache[mIndex] = ConditionState::kFalse;
- if (!mSliced) {
- mUnSlicedPart = ConditionState::kFalse;
- }
}
bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) {
@@ -305,9 +298,7 @@ void SimpleConditionTracker::evaluateCondition(
conditionCache[mIndex] =
itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
}
- mUnSlicedPart = conditionCache[mIndex];
}
-
return;
}
@@ -333,18 +324,12 @@ void SimpleConditionTracker::evaluateCondition(
}
conditionCache[mIndex] = overallState;
conditionChangedCache[mIndex] = overallChanged;
- if (!mSliced) {
- mUnSlicedPart = overallState;
- }
}
void SimpleConditionTracker::isConditionMet(
const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
const bool isPartialLink,
- vector<ConditionState>& conditionCache,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
+ vector<ConditionState>& conditionCache) const {
if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
// it has been evaluated.
@@ -356,18 +341,13 @@ void SimpleConditionTracker::isConditionMet(
if (pair == conditionParameters.end()) {
ConditionState conditionState = ConditionState::kNotEvaluated;
- if (dimensionFields.size() > 0 && dimensionFields[0].mMatcher.getTag() == mDimensionTag) {
- conditionState = conditionState | getMetConditionDimension(
- allConditions, dimensionFields, isSubOutputDimensionFields, dimensionsKeySet);
- } else {
- conditionState = conditionState | mInitialValue;
- if (!mSliced) {
- const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
- if (itr != mSlicedConditionState.end()) {
- ConditionState sliceState =
- itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
- conditionState = conditionState | sliceState;
- }
+ conditionState = conditionState | mInitialValue;
+ if (!mSliced) {
+ const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
+ if (itr != mSlicedConditionState.end()) {
+ ConditionState sliceState =
+ itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+ conditionState = conditionState | sliceState;
}
}
conditionCache[mIndex] = conditionState;
@@ -385,15 +365,6 @@ void SimpleConditionTracker::isConditionMet(
slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
if (slice.first.contains(key)) {
conditionState = conditionState | sliceState;
- if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) {
- if (isSubOutputDimensionFields) {
- HashableDimensionKey dimensionKey;
- filterValues(dimensionFields, slice.first.getValues(), &dimensionKey);
- dimensionsKeySet.insert(dimensionKey);
- } else {
- dimensionsKeySet.insert(slice.first);
- }
- }
}
}
} else {
@@ -403,15 +374,6 @@ void SimpleConditionTracker::isConditionMet(
ConditionState sliceState =
startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
conditionState = conditionState | sliceState;
- if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) {
- if (isSubOutputDimensionFields) {
- HashableDimensionKey dimensionKey;
- filterValues(dimensionFields, startedCountIt->first.getValues(), &dimensionKey);
- dimensionsKeySet.insert(dimensionKey);
- } else {
- dimensionsKeySet.insert(startedCountIt->first);
- }
- }
}
}
@@ -419,41 +381,6 @@ void SimpleConditionTracker::isConditionMet(
VLOG("Predicate %lld return %d", (long long)mConditionId, conditionCache[mIndex]);
}
-ConditionState SimpleConditionTracker::getMetConditionDimension(
- const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
- ConditionState conditionState = mInitialValue;
- if (dimensionFields.size() == 0 || mOutputDimensions.size() == 0 ||
- dimensionFields[0].mMatcher.getTag() != mOutputDimensions[0].mMatcher.getTag()) {
- const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
- if (itr != mSlicedConditionState.end()) {
- ConditionState sliceState =
- itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
- conditionState = conditionState | sliceState;
- }
- return conditionState;
- }
-
- for (const auto& slice : mSlicedConditionState) {
- ConditionState sliceState =
- slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
- conditionState = conditionState | sliceState;
-
- if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) {
- if (isSubOutputDimensionFields) {
- HashableDimensionKey dimensionKey;
- filterValues(dimensionFields, slice.first.getValues(), &dimensionKey);
- dimensionsKeySet.insert(dimensionKey);
- } else {
- dimensionsKeySet.insert(slice.first);
- }
- }
- }
- return conditionState;
-}
-
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index 47d1eceb9022..ea7f87bde2b8 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -37,8 +37,8 @@ public:
bool init(const std::vector<Predicate>& allConditionConfig,
const std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& conditionIdIndexMap,
- std::vector<bool>& stack) override;
+ const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack,
+ std::vector<ConditionState>& initialConditionCache) override;
void evaluateCondition(const LogEvent& event,
const std::vector<MatchingState>& eventMatcherValues,
@@ -48,17 +48,8 @@ public:
void isConditionMet(const ConditionKey& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
const bool isPartialLink,
- std::vector<ConditionState>& conditionCache,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
-
- ConditionState getMetConditionDimension(
- const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
+ std::vector<ConditionState>& conditionCache) const override;
virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
const std::vector<sp<ConditionTracker>>& allConditions) const {
diff --git a/cmds/statsd/src/condition/StateTracker.cpp b/cmds/statsd/src/condition/StateTracker.cpp
deleted file mode 100644
index 1965ce6015ff..000000000000
--- a/cmds/statsd/src/condition/StateTracker.cpp
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright 2018, 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.
- */
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "StateTracker.h"
-#include "guardrail/StatsdStats.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::string;
-using std::unordered_set;
-using std::vector;
-
-StateTracker::StateTracker(const ConfigKey& key, const int64_t& id, const int index,
- const SimplePredicate& simplePredicate,
- const unordered_map<int64_t, int>& trackerNameIndexMap,
- const vector<Matcher> primaryKeys)
- : ConditionTracker(id, index), mConfigKey(key), mPrimaryKeys(primaryKeys) {
- if (simplePredicate.has_start()) {
- auto pair = trackerNameIndexMap.find(simplePredicate.start());
- if (pair == trackerNameIndexMap.end()) {
- ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start());
- return;
- }
- mStartLogMatcherIndex = pair->second;
- mTrackerIndex.insert(mStartLogMatcherIndex);
- } else {
- ALOGW("Condition %lld must have a start matcher", (long long)id);
- return;
- }
-
- if (simplePredicate.has_dimensions()) {
- translateFieldMatcher(simplePredicate.dimensions(), &mOutputDimensions);
- if (mOutputDimensions.size() > 0) {
- mSliced = true;
- mDimensionTag = mOutputDimensions[0].mMatcher.getTag();
- } else {
- ALOGW("Condition %lld has invalid dimensions", (long long)id);
- return;
- }
- } else {
- ALOGW("Condition %lld being a state tracker, but has no dimension", (long long)id);
- return;
- }
-
- if (simplePredicate.initial_value() == SimplePredicate_InitialValue_FALSE) {
- mInitialValue = ConditionState::kFalse;
- } else {
- mInitialValue = ConditionState::kUnknown;
- }
-
- mNonSlicedConditionState = mInitialValue;
- mInitialized = true;
-}
-
-StateTracker::~StateTracker() {
- VLOG("~StateTracker()");
-}
-
-bool StateTracker::init(const vector<Predicate>& allConditionConfig,
- const vector<sp<ConditionTracker>>& allConditionTrackers,
- const unordered_map<int64_t, int>& conditionIdIndexMap,
- vector<bool>& stack) {
- return mInitialized;
-}
-
-void StateTracker::dumpState() {
- VLOG("StateTracker %lld DUMP:", (long long)mConditionId);
- for (const auto& value : mSlicedState) {
- VLOG("\t%s -> %s", value.first.toString().c_str(), value.second.toString().c_str());
- }
- VLOG("Last Changed to True: ");
- for (const auto& value : mLastChangedToTrueDimensions) {
- VLOG("%s", value.toString().c_str());
- }
- VLOG("Last Changed to False: ");
- for (const auto& value : mLastChangedToFalseDimensions) {
- VLOG("%s", value.toString().c_str());
- }
-}
-
-bool StateTracker::hitGuardRail(const HashableDimensionKey& newKey) {
- if (mSlicedState.find(newKey) != mSlicedState.end()) {
- // if the condition is not sliced or the key is not new, we are good!
- return false;
- }
- // 1. Report the tuple count if the tuple count > soft limit
- if (mSlicedState.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
- size_t newTupleCount = mSlicedState.size() + 1;
- StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mConditionId, newTupleCount);
- // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
- if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
- ALOGE("Predicate %lld dropping data for dimension key %s",
- (long long)mConditionId, newKey.toString().c_str());
- return true;
- }
- }
- return false;
-}
-
-void StateTracker::evaluateCondition(const LogEvent& event,
- const vector<MatchingState>& eventMatcherValues,
- const vector<sp<ConditionTracker>>& mAllConditions,
- vector<ConditionState>& conditionCache,
- vector<bool>& conditionChangedCache) {
- mLastChangedToTrueDimensions.clear();
- mLastChangedToFalseDimensions.clear();
- if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
- // it has been evaluated.
- VLOG("Yes, already evaluated, %lld %d", (long long)mConditionId, conditionCache[mIndex]);
- return;
- }
-
- if (mStartLogMatcherIndex >= 0 &&
- eventMatcherValues[mStartLogMatcherIndex] != MatchingState::kMatched) {
- conditionCache[mIndex] =
- mSlicedState.size() > 0 ? ConditionState::kTrue : ConditionState::kFalse;
- conditionChangedCache[mIndex] = false;
- return;
- }
-
- VLOG("StateTracker evaluate event %s", event.ToString().c_str());
-
- // Primary key can exclusive fields must be simple fields. so there won't be more than
- // one keys matched.
- HashableDimensionKey primaryKey;
- HashableDimensionKey state;
- if ((mPrimaryKeys.size() > 0 && !filterValues(mPrimaryKeys, event.getValues(), &primaryKey)) ||
- !filterValues(mOutputDimensions, event.getValues(), &state)) {
- ALOGE("Failed to filter fields in the event?? panic now!");
- conditionCache[mIndex] =
- mSlicedState.size() > 0 ? ConditionState::kTrue : ConditionState::kFalse;
- conditionChangedCache[mIndex] = false;
- return;
- }
- hitGuardRail(primaryKey);
-
- VLOG("StateTracker: key %s state %s", primaryKey.toString().c_str(), state.toString().c_str());
-
- auto it = mSlicedState.find(primaryKey);
- if (it == mSlicedState.end()) {
- mSlicedState[primaryKey] = state;
- conditionCache[mIndex] = ConditionState::kTrue;
- mLastChangedToTrueDimensions.insert(state);
- conditionChangedCache[mIndex] = true;
- } else if (!(it->second == state)) {
- mLastChangedToFalseDimensions.insert(it->second);
- mLastChangedToTrueDimensions.insert(state);
- mSlicedState[primaryKey] = state;
- conditionCache[mIndex] = ConditionState::kTrue;
- conditionChangedCache[mIndex] = true;
- } else {
- conditionCache[mIndex] = ConditionState::kTrue;
- conditionChangedCache[mIndex] = false;
- }
-
- if (DEBUG) {
- dumpState();
- }
- return;
-}
-
-void StateTracker::isConditionMet(
- const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- const bool isPartialLink,
- vector<ConditionState>& conditionCache,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
- if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
- // it has been evaluated.
- VLOG("Yes, already evaluated, %lld %d", (long long)mConditionId, conditionCache[mIndex]);
- return;
- }
-
- const auto pair = conditionParameters.find(mConditionId);
- if (pair == conditionParameters.end()) {
- if (mSlicedState.size() > 0) {
- conditionCache[mIndex] = ConditionState::kTrue;
-
- for (const auto& state : mSlicedState) {
- dimensionsKeySet.insert(state.second);
- }
- } else {
- conditionCache[mIndex] = ConditionState::kUnknown;
- }
- return;
- }
-
- const auto& primaryKey = pair->second;
- conditionCache[mIndex] = mInitialValue;
- auto it = mSlicedState.find(primaryKey);
- if (it != mSlicedState.end()) {
- conditionCache[mIndex] = ConditionState::kTrue;
- dimensionsKeySet.insert(it->second);
- }
-}
-
-ConditionState StateTracker::getMetConditionDimension(
- const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
- if (mSlicedState.size() > 0) {
- for (const auto& state : mSlicedState) {
- dimensionsKeySet.insert(state.second);
- }
- return ConditionState::kTrue;
- }
-
- return mInitialValue;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android \ No newline at end of file
diff --git a/cmds/statsd/src/condition/StateTracker.h b/cmds/statsd/src/condition/StateTracker.h
deleted file mode 100644
index 2bdf98c34c32..000000000000
--- a/cmds/statsd/src/condition/StateTracker.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright 2018, 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 <gtest/gtest_prod.h>
-#include "ConditionTracker.h"
-#include "config/ConfigKey.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "stats_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class StateTracker : public virtual ConditionTracker {
-public:
- StateTracker(const ConfigKey& key, const int64_t& id, const int index,
- const SimplePredicate& simplePredicate,
- const std::unordered_map<int64_t, int>& trackerNameIndexMap,
- const vector<Matcher> primaryKeys);
-
- ~StateTracker();
-
- bool init(const std::vector<Predicate>& allConditionConfig,
- const std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& conditionIdIndexMap,
- std::vector<bool>& stack) override;
-
- void evaluateCondition(const LogEvent& event,
- const std::vector<MatchingState>& eventMatcherValues,
- const std::vector<sp<ConditionTracker>>& mAllConditions,
- std::vector<ConditionState>& conditionCache,
- std::vector<bool>& changedCache) override;
-
- /**
- * Note: dimensionFields will be ignored in StateTracker, because we demand metrics
- * must take the entire dimension fields from StateTracker. This is to make implementation
- * simple and efficient.
- *
- * For example: wakelock duration by uid process states:
- * dimension in condition must be {uid, process state}.
- */
- void isConditionMet(const ConditionKey& conditionParameters,
- const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- const bool isPartialLink,
- std::vector<ConditionState>& conditionCache,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
-
- /**
- * Note: dimensionFields will be ignored in StateTracker, because we demand metrics
- * must take the entire dimension fields from StateTracker. This is to make implementation
- * simple and efficient.
- */
- ConditionState getMetConditionDimension(
- const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
-
- virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions) const {
- return &mLastChangedToTrueDimensions;
- }
-
- virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions) const {
- return &mLastChangedToFalseDimensions;
- }
-
- bool IsChangedDimensionTrackable() const override { return true; }
-
- bool IsSimpleCondition() const override { return true; }
-
- bool equalOutputDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensions) const override {
- return equalDimensions(mOutputDimensions, dimensions);
- }
-
- void getTrueSlicedDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions,
- std::set<HashableDimensionKey>* dimensions) const override {
- for (const auto& itr : mSlicedState) {
- dimensions->insert(itr.second);
- }
- }
-
-private:
- const ConfigKey mConfigKey;
-
- // The index of the LogEventMatcher which defines the start.
- int mStartLogMatcherIndex;
-
- std::set<HashableDimensionKey> mLastChangedToTrueDimensions;
- std::set<HashableDimensionKey> mLastChangedToFalseDimensions;
-
- std::vector<Matcher> mOutputDimensions;
- std::vector<Matcher> mPrimaryKeys;
-
- ConditionState mInitialValue;
-
- int mDimensionTag;
-
- void dumpState();
-
- bool hitGuardRail(const HashableDimensionKey& newKey);
-
- // maps from [primary_key] to [primary_key, exclusive_state].
- std::unordered_map<HashableDimensionKey, HashableDimensionKey> mSlicedState;
-
- FRIEND_TEST(StateTrackerTest, TestStateChange);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android \ No newline at end of file
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 13d251ab7736..13020e06dc5d 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -42,7 +42,78 @@ using std::vector;
using android::base::StringPrintf;
using std::unique_ptr;
-ConfigManager::ConfigManager() {
+struct ConfigReceiverDeathCookie {
+ ConfigReceiverDeathCookie(const wp<ConfigManager>& configManager, const ConfigKey& configKey,
+ const shared_ptr<IPendingIntentRef>& pir) :
+ mConfigManager(configManager), mConfigKey(configKey), mPir(pir) {
+ }
+
+ wp<ConfigManager> mConfigManager;
+ ConfigKey mConfigKey;
+ shared_ptr<IPendingIntentRef> mPir;
+};
+
+void ConfigManager::configReceiverDied(void* cookie) {
+ auto cookie_ = static_cast<ConfigReceiverDeathCookie*>(cookie);
+ sp<ConfigManager> thiz = cookie_->mConfigManager.promote();
+ if (!thiz) {
+ return;
+ }
+
+ ConfigKey& configKey = cookie_->mConfigKey;
+ shared_ptr<IPendingIntentRef>& pir = cookie_->mPir;
+
+ // Erase the mapping from the config key to the config receiver (pir) if the
+ // mapping still exists.
+ lock_guard<mutex> lock(thiz->mMutex);
+ auto it = thiz->mConfigReceivers.find(configKey);
+ if (it != thiz->mConfigReceivers.end() && it->second == pir) {
+ thiz->mConfigReceivers.erase(configKey);
+ }
+
+ // The death recipient corresponding to this specific pir can never be
+ // triggered again, so free up resources.
+ delete cookie_;
+}
+
+struct ActiveConfigChangedReceiverDeathCookie {
+ ActiveConfigChangedReceiverDeathCookie(const wp<ConfigManager>& configManager, const int uid,
+ const shared_ptr<IPendingIntentRef>& pir) :
+ mConfigManager(configManager), mUid(uid), mPir(pir) {
+ }
+
+ wp<ConfigManager> mConfigManager;
+ int mUid;
+ shared_ptr<IPendingIntentRef> mPir;
+};
+
+void ConfigManager::activeConfigChangedReceiverDied(void* cookie) {
+ auto cookie_ = static_cast<ActiveConfigChangedReceiverDeathCookie*>(cookie);
+ sp<ConfigManager> thiz = cookie_->mConfigManager.promote();
+ if (!thiz) {
+ return;
+ }
+
+ int uid = cookie_->mUid;
+ shared_ptr<IPendingIntentRef>& pir = cookie_->mPir;
+
+ // Erase the mapping from the config key to the active config changed
+ // receiver (pir) if the mapping still exists.
+ lock_guard<mutex> lock(thiz->mMutex);
+ auto it = thiz->mActiveConfigsChangedReceivers.find(uid);
+ if (it != thiz->mActiveConfigsChangedReceivers.end() && it->second == pir) {
+ thiz->mActiveConfigsChangedReceivers.erase(uid);
+ }
+
+ // The death recipient corresponding to this specific pir can never
+ // be triggered again, so free up resources.
+ delete cookie_;
+}
+
+ConfigManager::ConfigManager() :
+ mConfigReceiverDeathRecipient(AIBinder_DeathRecipient_new(configReceiverDied)),
+ mActiveConfigChangedReceiverDeathRecipient(
+ AIBinder_DeathRecipient_new(activeConfigChangedReceiverDied)) {
}
ConfigManager::~ConfigManager() {
@@ -114,9 +185,12 @@ void ConfigManager::UpdateConfig(const ConfigKey& key, const StatsdConfig& confi
}
}
-void ConfigManager::SetConfigReceiver(const ConfigKey& key, const sp<IBinder>& intentSender) {
+void ConfigManager::SetConfigReceiver(const ConfigKey& key,
+ const shared_ptr<IPendingIntentRef>& pir) {
lock_guard<mutex> lock(mMutex);
- mConfigReceivers[key] = intentSender;
+ mConfigReceivers[key] = pir;
+ AIBinder_linkToDeath(pir->asBinder().get(), mConfigReceiverDeathRecipient.get(),
+ new ConfigReceiverDeathCookie(this, key, pir));
}
void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) {
@@ -125,9 +199,13 @@ void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) {
}
void ConfigManager::SetActiveConfigsChangedReceiver(const int uid,
- const sp<IBinder>& intentSender) {
- lock_guard<mutex> lock(mMutex);
- mActiveConfigsChangedReceivers[uid] = intentSender;
+ const shared_ptr<IPendingIntentRef>& pir) {
+ {
+ lock_guard<mutex> lock(mMutex);
+ mActiveConfigsChangedReceivers[uid] = pir;
+ }
+ AIBinder_linkToDeath(pir->asBinder().get(), mActiveConfigChangedReceiverDeathRecipient.get(),
+ new ActiveConfigChangedReceiverDeathCookie(this, uid, pir));
}
void ConfigManager::RemoveActiveConfigsChangedReceiver(const int uid) {
@@ -146,25 +224,11 @@ void ConfigManager::RemoveConfig(const ConfigKey& key) {
// Remove from map
uidIt->second.erase(key);
- // No more configs for this uid, lets remove the active configs callback.
- if (uidIt->second.empty()) {
- auto itActiveConfigsChangedReceiver = mActiveConfigsChangedReceivers.find(uid);
- if (itActiveConfigsChangedReceiver != mActiveConfigsChangedReceivers.end()) {
- mActiveConfigsChangedReceivers.erase(itActiveConfigsChangedReceiver);
- }
- }
-
for (const sp<ConfigListener>& listener : mListeners) {
broadcastList.push_back(listener);
}
}
- auto itReceiver = mConfigReceivers.find(key);
- if (itReceiver != mConfigReceivers.end()) {
- // Remove from map
- mConfigReceivers.erase(itReceiver);
- }
-
// Remove from disk. There can still be a lingering file on disk so we check
// whether or not the config was on memory.
remove_saved_configs(key);
@@ -195,12 +259,6 @@ void ConfigManager::RemoveConfigs(int uid) {
// Remove from map
remove_saved_configs(*it);
removed.push_back(*it);
- mConfigReceivers.erase(*it);
- }
-
- auto itActiveConfigsChangedReceiver = mActiveConfigsChangedReceivers.find(uid);
- if (itActiveConfigsChangedReceiver != mActiveConfigsChangedReceivers.end()) {
- mActiveConfigsChangedReceivers.erase(itActiveConfigsChangedReceiver);
}
mConfigs.erase(uidIt);
@@ -234,8 +292,6 @@ void ConfigManager::RemoveAllConfigs() {
uidIt = mConfigs.erase(uidIt);
}
- mConfigReceivers.clear();
- mActiveConfigsChangedReceivers.clear();
for (const sp<ConfigListener>& listener : mListeners) {
broadcastList.push_back(listener);
}
@@ -262,7 +318,7 @@ vector<ConfigKey> ConfigManager::GetAllConfigKeys() const {
return ret;
}
-const sp<android::IBinder> ConfigManager::GetConfigReceiver(const ConfigKey& key) const {
+const shared_ptr<IPendingIntentRef> ConfigManager::GetConfigReceiver(const ConfigKey& key) const {
lock_guard<mutex> lock(mMutex);
auto it = mConfigReceivers.find(key);
@@ -273,7 +329,8 @@ const sp<android::IBinder> ConfigManager::GetConfigReceiver(const ConfigKey& key
}
}
-const sp<android::IBinder> ConfigManager::GetActiveConfigsChangedReceiver(const int uid) const {
+const shared_ptr<IPendingIntentRef> ConfigManager::GetActiveConfigsChangedReceiver(const int uid)
+ const {
lock_guard<mutex> lock(mMutex);
auto it = mActiveConfigsChangedReceivers.find(uid);
diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h
index 1fdec316e499..bef057f96409 100644
--- a/cmds/statsd/src/config/ConfigManager.h
+++ b/cmds/statsd/src/config/ConfigManager.h
@@ -16,16 +16,18 @@
#pragma once
-#include "binder/IBinder.h"
#include "config/ConfigKey.h"
#include "config/ConfigListener.h"
-#include <map>
+#include <aidl/android/os/IPendingIntentRef.h>
#include <mutex>
#include <string>
#include <stdio.h>
+using aidl::android::os::IPendingIntentRef;
+using std::shared_ptr;
+
namespace android {
namespace os {
namespace statsd {
@@ -63,12 +65,12 @@ public:
/**
* Sets the broadcast receiver for a configuration key.
*/
- void SetConfigReceiver(const ConfigKey& key, const sp<IBinder>& intentSender);
+ void SetConfigReceiver(const ConfigKey& key, const shared_ptr<IPendingIntentRef>& pir);
/**
* Returns the package name and class name representing the broadcast receiver for this config.
*/
- const sp<android::IBinder> GetConfigReceiver(const ConfigKey& key) const;
+ const shared_ptr<IPendingIntentRef> GetConfigReceiver(const ConfigKey& key) const;
/**
* Returns all config keys registered.
@@ -84,13 +86,13 @@ public:
* Sets the broadcast receiver that is notified whenever the list of active configs
* changes for this uid.
*/
- void SetActiveConfigsChangedReceiver(const int uid, const sp<IBinder>& intentSender);
+ void SetActiveConfigsChangedReceiver(const int uid, const shared_ptr<IPendingIntentRef>& pir);
/**
* Returns the broadcast receiver for active configs changed for this uid.
*/
- const sp<IBinder> GetActiveConfigsChangedReceiver(const int uid) const;
+ const shared_ptr<IPendingIntentRef> GetActiveConfigsChangedReceiver(const int uid) const;
/**
* Erase any active configs changed broadcast receiver associated with this uid.
@@ -140,21 +142,38 @@ private:
std::map<int, std::set<ConfigKey>> mConfigs;
/**
- * Each config key can be subscribed by up to one receiver, specified as IBinder from
- * PendingIntent.
+ * Each config key can be subscribed by up to one receiver, specified as IPendingIntentRef.
*/
- std::map<ConfigKey, sp<android::IBinder>> mConfigReceivers;
+ std::map<ConfigKey, shared_ptr<IPendingIntentRef>> mConfigReceivers;
/**
* Each uid can be subscribed by up to one receiver to notify that the list of active configs
- * for this uid has changed. The receiver is specified as IBinder from PendingIntent.
+ * for this uid has changed. The receiver is specified as IPendingIntentRef.
*/
- std::map<int, sp<android::IBinder>> mActiveConfigsChangedReceivers;
+ std::map<int, shared_ptr<IPendingIntentRef>> mActiveConfigsChangedReceivers;
/**
* The ConfigListeners that will be told about changes.
*/
std::vector<sp<ConfigListener>> mListeners;
+
+ // Death recipients that are triggered when the host process holding an
+ // IPendingIntentRef dies.
+ ::ndk::ScopedAIBinder_DeathRecipient mConfigReceiverDeathRecipient;
+ ::ndk::ScopedAIBinder_DeathRecipient mActiveConfigChangedReceiverDeathRecipient;
+
+ /**
+ * Death recipient callback that is called when a config receiver dies.
+ * The cookie is a pointer to a ConfigReceiverDeathCookie.
+ */
+ static void configReceiverDied(void* cookie);
+
+ /**
+ * Death recipient callback that is called when an active config changed
+ * receiver dies. The cookie is a pointer to an
+ * ActiveConfigChangedReceiverDeathCookie.
+ */
+ static void activeConfigChangedReceiverDied(void* cookie);
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/CarStatsPuller.h b/cmds/statsd/src/experiment_ids.proto
index ca0f1a9c9a17..c2036314cf58 100644
--- a/cmds/statsd/src/external/CarStatsPuller.h
+++ b/cmds/statsd/src/experiment_ids.proto
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,23 +14,16 @@
* limitations under the License.
*/
-#pragma once
+syntax = "proto2";
-#include "StatsPuller.h"
+package android.os.statsd;
-namespace android {
-namespace os {
-namespace statsd {
+option java_package = "com.android.internal.os";
+option java_outer_classname = "ExperimentIdsProto";
-/**
- * Pull atoms from CarService.
- */
-class CarStatsPuller : public StatsPuller {
-public:
- explicit CarStatsPuller(const int tagId);
- bool PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) override;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
+// StatsLogProcessor uses the proto to parse experiment ids from
+// BinaryPushStateChanged atoms. This needs to be in sync with
+// TrainExperimentIds in atoms.proto.
+message ExperimentIds {
+ repeated int64 experiment_id = 1;
+}
diff --git a/cmds/statsd/src/external/CarStatsPuller.cpp b/cmds/statsd/src/external/CarStatsPuller.cpp
deleted file mode 100644
index 70c0456b5eb4..000000000000
--- a/cmds/statsd/src/external/CarStatsPuller.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright 2019 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.
- */
-
-#define DEBUG false
-#include "Log.h"
-
-#include <binder/IServiceManager.h>
-#include <com/android/internal/car/ICarStatsService.h>
-
-#include "CarStatsPuller.h"
-#include "logd/LogEvent.h"
-#include "stats_log_util.h"
-
-using android::binder::Status;
-using com::android::internal::car::ICarStatsService;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-static std::mutex gCarStatsMutex;
-static sp<ICarStatsService> gCarStats = nullptr;
-
-class CarStatsDeathRecipient : public android::IBinder::DeathRecipient {
- public:
- CarStatsDeathRecipient() = default;
- ~CarStatsDeathRecipient() override = default;
-
- // android::IBinder::DeathRecipient override:
- void binderDied(const android::wp<android::IBinder>& /* who */) override {
- ALOGE("Car service has died");
- std::lock_guard<std::mutex> lock(gCarStatsMutex);
- if (gCarStats) {
- sp<IBinder> binder = IInterface::asBinder(gCarStats);
- binder->unlinkToDeath(this);
- gCarStats = nullptr;
- }
- }
-};
-
-static sp<CarStatsDeathRecipient> gDeathRecipient = new CarStatsDeathRecipient();
-
-static sp<ICarStatsService> getCarService() {
- std::lock_guard<std::mutex> lock(gCarStatsMutex);
- if (!gCarStats) {
- const sp<IBinder> binder = defaultServiceManager()->checkService(String16("car_stats"));
- if (!binder) {
- ALOGW("Car service is unavailable");
- return nullptr;
- }
- gCarStats = interface_cast<ICarStatsService>(binder);
- binder->linkToDeath(gDeathRecipient);
- }
- return gCarStats;
-}
-
-CarStatsPuller::CarStatsPuller(const int tagId) : StatsPuller(tagId) {
-}
-
-bool CarStatsPuller::PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) {
- const sp<ICarStatsService> carService = getCarService();
- if (!carService) {
- return false;
- }
-
- vector<StatsLogEventWrapper> returned_value;
- Status status = carService->pullData(mTagId, &returned_value);
- if (!status.isOk()) {
- ALOGW("CarStatsPuller::pull failed for %d", mTagId);
- return false;
- }
-
- data->clear();
- for (const StatsLogEventWrapper& it : returned_value) {
- LogEvent::createLogEvents(it, *data);
- }
- VLOG("CarStatsPuller::pull succeeded for %d", mTagId);
- return true;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/GpuStatsPuller.cpp b/cmds/statsd/src/external/GpuStatsPuller.cpp
deleted file mode 100644
index 0d3aca05e0e5..000000000000
--- a/cmds/statsd/src/external/GpuStatsPuller.cpp
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright 2019 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 "GpuStatsPuller.h"
-
-#include <binder/IServiceManager.h>
-#include <graphicsenv/GpuStatsInfo.h>
-#include <graphicsenv/IGpuService.h>
-
-#include "logd/LogEvent.h"
-
-#include "stats_log_util.h"
-#include "statslog.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using android::util::ProtoReader;
-
-GpuStatsPuller::GpuStatsPuller(const int tagId) : StatsPuller(tagId) {
-}
-
-static sp<IGpuService> getGpuService() {
- const sp<IBinder> binder = defaultServiceManager()->checkService(String16("gpu"));
- if (!binder) {
- ALOGE("Failed to get gpu service");
- return nullptr;
- }
-
- return interface_cast<IGpuService>(binder);
-}
-
-static bool pullGpuStatsGlobalInfo(const sp<IGpuService>& gpuService,
- std::vector<std::shared_ptr<LogEvent>>* data) {
- std::vector<GpuStatsGlobalInfo> stats;
- status_t status = gpuService->getGpuStatsGlobalInfo(&stats);
- if (status != OK) {
- return false;
- }
-
- data->clear();
- data->reserve(stats.size());
- for (const auto& info : stats) {
- std::shared_ptr<LogEvent> event = make_shared<LogEvent>(
- android::util::GPU_STATS_GLOBAL_INFO, getWallClockNs(), getElapsedRealtimeNs());
- if (!event->write(info.driverPackageName)) return false;
- if (!event->write(info.driverVersionName)) return false;
- if (!event->write((int64_t)info.driverVersionCode)) return false;
- if (!event->write(info.driverBuildTime)) return false;
- if (!event->write((int64_t)info.glLoadingCount)) return false;
- if (!event->write((int64_t)info.glLoadingFailureCount)) return false;
- if (!event->write((int64_t)info.vkLoadingCount)) return false;
- if (!event->write((int64_t)info.vkLoadingFailureCount)) return false;
- if (!event->write(info.vulkanVersion)) return false;
- if (!event->write(info.cpuVulkanVersion)) return false;
- if (!event->write(info.glesVersion)) return false;
- if (!event->write((int64_t)info.angleLoadingCount)) return false;
- if (!event->write((int64_t)info.angleLoadingFailureCount)) return false;
- event->init();
- data->emplace_back(event);
- }
-
- return true;
-}
-
-static bool pullGpuStatsAppInfo(const sp<IGpuService>& gpuService,
- std::vector<std::shared_ptr<LogEvent>>* data) {
- std::vector<GpuStatsAppInfo> stats;
- status_t status = gpuService->getGpuStatsAppInfo(&stats);
- if (status != OK) {
- return false;
- }
-
- data->clear();
- data->reserve(stats.size());
- for (const auto& info : stats) {
- std::shared_ptr<LogEvent> event = make_shared<LogEvent>(
- android::util::GPU_STATS_APP_INFO, getWallClockNs(), getElapsedRealtimeNs());
- if (!event->write(info.appPackageName)) return false;
- if (!event->write((int64_t)info.driverVersionCode)) return false;
- if (!event->write(int64VectorToProtoByteString(info.glDriverLoadingTime))) return false;
- if (!event->write(int64VectorToProtoByteString(info.vkDriverLoadingTime))) return false;
- if (!event->write(int64VectorToProtoByteString(info.angleDriverLoadingTime))) return false;
- if (!event->write(info.cpuVulkanInUse)) return false;
- event->init();
- data->emplace_back(event);
- }
-
- return true;
-}
-
-bool GpuStatsPuller::PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) {
- const sp<IGpuService> gpuService = getGpuService();
- if (!gpuService) {
- return false;
- }
-
- switch (mTagId) {
- case android::util::GPU_STATS_GLOBAL_INFO:
- return pullGpuStatsGlobalInfo(gpuService, data);
- case android::util::GPU_STATS_APP_INFO:
- return pullGpuStatsAppInfo(gpuService, data);
- default:
- break;
- }
-
- return false;
-}
-
-static std::string protoOutputStreamToByteString(ProtoOutputStream& proto) {
- if (!proto.size()) return "";
-
- std::string byteString;
- sp<ProtoReader> reader = proto.data();
- while (reader->readBuffer() != nullptr) {
- const size_t toRead = reader->currentToRead();
- byteString.append((char*)reader->readBuffer(), toRead);
- reader->move(toRead);
- }
-
- if (byteString.size() != proto.size()) return "";
-
- return byteString;
-}
-
-std::string int64VectorToProtoByteString(const std::vector<int64_t>& value) {
- if (value.empty()) return "";
-
- ProtoOutputStream proto;
- for (const auto& ele : value) {
- proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
- 1 /* field id */,
- (long long)ele);
- }
-
- return protoOutputStreamToByteString(proto);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/Perfetto.h b/cmds/statsd/src/external/Perfetto.h
index ab2c1954e2a0..095782a49f9b 100644
--- a/cmds/statsd/src/external/Perfetto.h
+++ b/cmds/statsd/src/external/Perfetto.h
@@ -16,10 +16,6 @@
#pragma once
-#include <android/os/StatsLogEventWrapper.h>
-
-using android::os::StatsLogEventWrapper;
-
namespace android {
namespace os {
namespace statsd {
diff --git a/cmds/statsd/src/external/PowerStatsPuller.cpp b/cmds/statsd/src/external/PowerStatsPuller.cpp
deleted file mode 100644
index dc69b78f0329..000000000000
--- a/cmds/statsd/src/external/PowerStatsPuller.cpp
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include <android/hardware/power/stats/1.0/IPowerStats.h>
-
-#include <vector>
-
-#include "PowerStatsPuller.h"
-#include "statslog.h"
-#include "stats_log_util.h"
-
-using android::hardware::hidl_vec;
-using android::hardware::power::stats::V1_0::IPowerStats;
-using android::hardware::power::stats::V1_0::EnergyData;
-using android::hardware::power::stats::V1_0::RailInfo;
-using android::hardware::power::stats::V1_0::Status;
-using android::hardware::Return;
-using android::hardware::Void;
-
-using std::make_shared;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHal = nullptr;
-static std::mutex gPowerStatsHalMutex;
-static bool gPowerStatsExist = true; // Initialized to ensure making a first attempt.
-static std::vector<RailInfo> gRailInfo;
-
-struct PowerStatsPullerDeathRecipient : virtual public hardware::hidl_death_recipient {
- virtual void serviceDied(uint64_t cookie,
- const wp<android::hidl::base::V1_0::IBase>& who) override {
- // The HAL just died. Reset all handles to HAL services.
- std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
- gPowerStatsHal = nullptr;
- }
-};
-
-static sp<PowerStatsPullerDeathRecipient> gDeathRecipient = new PowerStatsPullerDeathRecipient();
-
-static bool getPowerStatsHalLocked() {
- if (gPowerStatsHal == nullptr && gPowerStatsExist) {
- gPowerStatsHal = android::hardware::power::stats::V1_0::IPowerStats::getService();
- if (gPowerStatsHal == nullptr) {
- ALOGW("Couldn't load power.stats HAL service");
- gPowerStatsExist = false;
- } else {
- // Link death recipient to power.stats service handle
- hardware::Return<bool> linked = gPowerStatsHal->linkToDeath(gDeathRecipient, 0);
- if (!linked.isOk()) {
- ALOGE("Transaction error in linking to power.stats HAL death: %s",
- linked.description().c_str());
- gPowerStatsHal = nullptr;
- return false;
- } else if (!linked) {
- ALOGW("Unable to link to power.stats HAL death notifications");
- // We should still continue even though linking failed
- }
- }
- }
- return gPowerStatsHal != nullptr;
-}
-
-PowerStatsPuller::PowerStatsPuller() : StatsPuller(android::util::ON_DEVICE_POWER_MEASUREMENT) {
-}
-
-bool PowerStatsPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
- std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
- if (!getPowerStatsHalLocked()) {
- return false;
- }
-
- int64_t wallClockTimestampNs = getWallClockNs();
- int64_t elapsedTimestampNs = getElapsedRealtimeNs();
-
- data->clear();
-
- // Pull getRailInfo if necessary
- if (gRailInfo.empty()) {
- bool resultSuccess = true;
- Return<void> ret = gPowerStatsHal->getRailInfo(
- [&resultSuccess](const hidl_vec<RailInfo> &list, Status status) {
- resultSuccess = (status == Status::SUCCESS || status == Status::NOT_SUPPORTED);
- if (status != Status::SUCCESS) return;
-
- gRailInfo.reserve(list.size());
- for (size_t i = 0; i < list.size(); ++i) {
- gRailInfo.push_back(list[i]);
- }
- });
- if (!resultSuccess || !ret.isOk()) {
- ALOGE("power.stats getRailInfo() failed. Description: %s", ret.description().c_str());
- gPowerStatsHal = nullptr;
- return false;
- }
- // If SUCCESS but empty, or if NOT_SUPPORTED, then never try again.
- if (gRailInfo.empty()) {
- ALOGE("power.stats has no rail information");
- gPowerStatsExist = false; // No rail info, so never try again.
- gPowerStatsHal = nullptr;
- return false;
- }
- }
-
- // Pull getEnergyData and write the data out
- const hidl_vec<uint32_t> desiredRailIndices; // Empty vector indicates we want all.
- bool resultSuccess = true;
- Return<void> ret = gPowerStatsHal->getEnergyData(desiredRailIndices,
- [&data, wallClockTimestampNs, elapsedTimestampNs, &resultSuccess]
- (hidl_vec<EnergyData> energyDataList, Status status) {
- resultSuccess = (status == Status::SUCCESS);
- if (!resultSuccess) return;
-
- for (size_t i = 0; i < energyDataList.size(); i++) {
- const EnergyData& energyData = energyDataList[i];
-
- if (energyData.index >= gRailInfo.size()) {
- ALOGE("power.stats getEnergyData() returned an invalid rail index %u.",
- energyData.index);
- resultSuccess = false;
- return;
- }
- const RailInfo& rail = gRailInfo[energyData.index];
-
- auto ptr = make_shared<LogEvent>(android::util::ON_DEVICE_POWER_MEASUREMENT,
- wallClockTimestampNs, elapsedTimestampNs);
- ptr->write(rail.subsysName);
- ptr->write(rail.railName);
- ptr->write(energyData.timestamp);
- ptr->write(energyData.energy);
- ptr->init();
- data->push_back(ptr);
-
- VLOG("power.stat: %s.%s: %llu, %llu",
- rail.subsysName.c_str(),
- rail.railName.c_str(),
- (unsigned long long)energyData.timestamp,
- (unsigned long long)energyData.energy);
- }
- });
- if (!resultSuccess || !ret.isOk()) {
- ALOGE("power.stats getEnergyData() failed. Description: %s", ret.description().c_str());
- gPowerStatsHal = nullptr;
- return false;
- }
- return true;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/GpuStatsPuller.h b/cmds/statsd/src/external/PullResultReceiver.cpp
index 2da199c51e0f..8aa4792dc179 100644
--- a/cmds/statsd/src/external/GpuStatsPuller.h
+++ b/cmds/statsd/src/external/PullResultReceiver.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -14,28 +14,25 @@
* limitations under the License.
*/
-#pragma once
-
-#include "StatsPuller.h"
+#include "PullResultReceiver.h"
namespace android {
namespace os {
namespace statsd {
-/**
- * Pull GpuStats from GpuService.
- */
-class GpuStatsPuller : public StatsPuller {
-public:
- explicit GpuStatsPuller(const int tagId);
- bool PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) override;
-};
+PullResultReceiver::PullResultReceiver(
+ std::function<void(int32_t, bool, const vector<StatsEventParcel>&)> pullFinishCb)
+ : pullFinishCallback(std::move(pullFinishCb)) {
+}
+
+Status PullResultReceiver::pullFinished(int32_t atomTag, bool success,
+ const vector<StatsEventParcel>& output) {
+ pullFinishCallback(atomTag, success, output);
+ return Status::ok();
+}
-// convert a int64_t vector into a byte string for proto message like:
-// message RepeatedInt64Wrapper {
-// repeated int64 value = 1;
-// }
-std::string int64VectorToProtoByteString(const std::vector<int64_t>& value);
+PullResultReceiver::~PullResultReceiver() {
+}
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/src/external/PullResultReceiver.h b/cmds/statsd/src/external/PullResultReceiver.h
new file mode 100644
index 000000000000..ceaae801b2d5
--- /dev/null
+++ b/cmds/statsd/src/external/PullResultReceiver.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 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 <aidl/android/os/BnPullAtomResultReceiver.h>
+#include <aidl/android/util/StatsEventParcel.h>
+
+using namespace std;
+
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::os::BnPullAtomResultReceiver;
+using aidl::android::util::StatsEventParcel;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class PullResultReceiver : public BnPullAtomResultReceiver {
+public:
+ PullResultReceiver(function<void(int32_t, bool, const vector<StatsEventParcel>&)>
+ pullFinishCallback);
+ ~PullResultReceiver();
+
+ /**
+ * Binder call for finishing a pull.
+ */
+ Status pullFinished(int32_t atomTag, bool success,
+ const vector<StatsEventParcel>& output) override;
+
+private:
+ function<void(int32_t, bool, const vector<StatsEventParcel>&)> pullFinishCallback;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/SubsystemSleepStatePuller.h b/cmds/statsd/src/external/PullUidProvider.h
index 87f5f02614a9..2318c501ea4b 100644
--- a/cmds/statsd/src/external/SubsystemSleepStatePuller.h
+++ b/cmds/statsd/src/external/PullUidProvider.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -13,25 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
#pragma once
-#include <utils/String16.h>
+#include <utils/RefBase.h>
+
#include "StatsPuller.h"
+#include "logd/LogEvent.h"
namespace android {
namespace os {
namespace statsd {
-/**
- * Reads hal for sleep states
- */
-class SubsystemSleepStatePuller : public StatsPuller {
+class PullUidProvider : virtual public RefBase {
public:
- SubsystemSleepStatePuller();
+ virtual ~PullUidProvider() {}
-private:
- bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+ /**
+ * @param atomId The atom for which to get the uids.
+ */
+ virtual vector<int32_t> getPullAtomUids(int32_t atomId) = 0;
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
deleted file mode 100644
index 75b63f4b5f9e..000000000000
--- a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include <android/hardware/health/2.0/IHealth.h>
-#include <healthhalutils/HealthHalUtils.h>
-#include "external/ResourceHealthManagerPuller.h"
-#include "external/StatsPuller.h"
-
-#include "ResourceHealthManagerPuller.h"
-#include "logd/LogEvent.h"
-#include "stats_log_util.h"
-#include "statslog.h"
-
-using android::hardware::hidl_vec;
-using android::hardware::Return;
-using android::hardware::Void;
-using android::hardware::health::V2_0::get_health_service;
-using android::hardware::health::V2_0::HealthInfo;
-using android::hardware::health::V2_0::IHealth;
-using android::hardware::health::V2_0::Result;
-
-using std::make_shared;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-sp<android::hardware::health::V2_0::IHealth> gHealthHal = nullptr;
-
-bool getHealthHal() {
- if (gHealthHal == nullptr) {
- gHealthHal = get_health_service();
- }
- return gHealthHal != nullptr;
-}
-
-ResourceHealthManagerPuller::ResourceHealthManagerPuller(int tagId) : StatsPuller(tagId) {
-}
-
-// TODO(b/110565992): add other health atoms (eg. Temperature).
-bool ResourceHealthManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
- if (!getHealthHal()) {
- ALOGE("Health Hal not loaded");
- return false;
- }
-
- int64_t wallClockTimestampNs = getWallClockNs();
- int64_t elapsedTimestampNs = getElapsedRealtimeNs();
-
- data->clear();
- bool result_success = true;
-
- // Get the data from the Health HAL (hardware/interfaces/health/1.0/types.hal).
- Return<void> ret = gHealthHal->getHealthInfo([&](Result r, HealthInfo v) {
- if (r != Result::SUCCESS) {
- result_success = false;
- return;
- }
- if (mTagId == android::util::REMAINING_BATTERY_CAPACITY) {
- auto ptr = make_shared<LogEvent>(android::util::REMAINING_BATTERY_CAPACITY,
- wallClockTimestampNs, elapsedTimestampNs);
- ptr->write(v.legacy.batteryChargeCounter);
- ptr->init();
- data->push_back(ptr);
- } else if (mTagId == android::util::FULL_BATTERY_CAPACITY) {
- auto ptr = make_shared<LogEvent>(android::util::FULL_BATTERY_CAPACITY,
- wallClockTimestampNs, elapsedTimestampNs);
- ptr->write(v.legacy.batteryFullCharge);
- ptr->init();
- data->push_back(ptr);
- } else if (mTagId == android::util::BATTERY_VOLTAGE) {
- auto ptr = make_shared<LogEvent>(android::util::BATTERY_VOLTAGE, wallClockTimestampNs,
- elapsedTimestampNs);
- ptr->write(v.legacy.batteryVoltage);
- ptr->init();
- data->push_back(ptr);
- } else if (mTagId == android::util::BATTERY_LEVEL) {
- auto ptr = make_shared<LogEvent>(android::util::BATTERY_LEVEL, wallClockTimestampNs,
- elapsedTimestampNs);
- ptr->write(v.legacy.batteryLevel);
- ptr->init();
- data->push_back(ptr);
- } else if (mTagId == android::util::BATTERY_CYCLE_COUNT) {
- auto ptr = make_shared<LogEvent>(android::util::BATTERY_CYCLE_COUNT,
- wallClockTimestampNs, elapsedTimestampNs);
- ptr->write(v.legacy.batteryCycleCount);
- ptr->init();
- data->push_back(ptr);
- } else {
- ALOGE("Unsupported tag in ResourceHealthManagerPuller: %d", mTagId);
- }
- });
- if (!result_success || !ret.isOk()) {
- ALOGE("getHealthHal() failed: health HAL service not available. Description: %s",
- ret.description().c_str());
- if (!ret.isOk() && ret.isDeadObject()) {
- gHealthHal = nullptr;
- }
- return false;
- }
- return true;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp
index d718273e9b85..78e6f094db7e 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.cpp
+++ b/cmds/statsd/src/external/StatsCallbackPuller.cpp
@@ -17,43 +17,98 @@
#define DEBUG false // STOPSHIP if true
#include "Log.h"
-#include <android/os/IStatsPullerCallback.h>
-
#include "StatsCallbackPuller.h"
+#include "PullResultReceiver.h"
+#include "StatsPullerManager.h"
#include "logd/LogEvent.h"
#include "stats_log_util.h"
-using namespace android::binder;
+#include <aidl/android/util/StatsEventParcel.h>
+
+using namespace std;
+
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::util::StatsEventParcel;
+using ::ndk::SharedRefBase;
namespace android {
namespace os {
namespace statsd {
-StatsCallbackPuller::StatsCallbackPuller(int tagId, const sp<IStatsPullerCallback>& callback) :
- StatsPuller(tagId), mCallback(callback) {
- VLOG("StatsCallbackPuller created for tag %d", tagId);
+StatsCallbackPuller::StatsCallbackPuller(int tagId, const shared_ptr<IPullAtomCallback>& callback,
+ const int64_t coolDownNs, int64_t timeoutNs,
+ const vector<int> additiveFields)
+ : StatsPuller(tagId, coolDownNs, timeoutNs, additiveFields), mCallback(callback) {
+ VLOG("StatsCallbackPuller created for tag %d", tagId);
}
bool StatsCallbackPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
- VLOG("StatsCallbackPuller called for tag %d", mTagId)
+ VLOG("StatsCallbackPuller called for tag %d", mTagId);
if(mCallback == nullptr) {
ALOGW("No callback registered");
return false;
}
- int64_t wallClockTimeNs = getWallClockNs();
- int64_t elapsedTimeNs = getElapsedRealtimeNs();
- vector<StatsLogEventWrapper> returned_value;
- Status status = mCallback->pullData(mTagId, elapsedTimeNs, wallClockTimeNs, &returned_value);
+
+ // Shared variables needed in the result receiver.
+ shared_ptr<mutex> cv_mutex = make_shared<mutex>();
+ shared_ptr<condition_variable> cv = make_shared<condition_variable>();
+ shared_ptr<bool> pullFinish = make_shared<bool>(false);
+ shared_ptr<bool> pullSuccess = make_shared<bool>(false);
+ shared_ptr<vector<shared_ptr<LogEvent>>> sharedData =
+ make_shared<vector<shared_ptr<LogEvent>>>();
+
+ shared_ptr<PullResultReceiver> resultReceiver = SharedRefBase::make<PullResultReceiver>(
+ [cv_mutex, cv, pullFinish, pullSuccess, sharedData](
+ int32_t atomTag, bool success, const vector<StatsEventParcel>& output) {
+ // This is the result of the pull, executing in a statsd binder thread.
+ // The pull could have taken a long time, and we should only modify
+ // data (the output param) if the pointer is in scope and the pull did not time out.
+ {
+ lock_guard<mutex> lk(*cv_mutex);
+ for (const StatsEventParcel& parcel: output) {
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(/*uid=*/-1, /*pid=*/-1);
+ bool valid = event->parseBuffer((uint8_t*)parcel.buffer.data(),
+ parcel.buffer.size());
+ if (valid) {
+ sharedData->push_back(event);
+ } else {
+ StatsdStats::getInstance().noteAtomError(event->GetTagId(),
+ /*pull=*/true);
+ }
+ }
+ *pullSuccess = success;
+ *pullFinish = true;
+ }
+ cv->notify_one();
+ });
+
+ // Initiate the pull. This is a oneway call to a different process, except
+ // in unit tests. In process calls are not oneway.
+ Status status = mCallback->onPullAtom(mTagId, resultReceiver);
if (!status.isOk()) {
- ALOGW("StatsCallbackPuller::pull failed for %d", mTagId);
+ StatsdStats::getInstance().notePullBinderCallFailed(mTagId);
return false;
}
- data->clear();
- for (const StatsLogEventWrapper& it: returned_value) {
- LogEvent::createLogEvents(it, *data);
+
+ {
+ unique_lock<mutex> unique_lk(*cv_mutex);
+ // Wait until the pull finishes, or until the pull timeout.
+ cv->wait_for(unique_lk, chrono::nanoseconds(mPullTimeoutNs),
+ [pullFinish] { return *pullFinish; });
+ if (!*pullFinish) {
+ // Note: The parent stats puller will also note that there was a timeout and that the
+ // cache should be cleared. Once we migrate all pullers to this callback, we could
+ // consolidate the logic.
+ return true;
+ } else {
+ // Only copy the data if we did not timeout and the pull was successful.
+ if (*pullSuccess) {
+ *data = std::move(*sharedData);
+ }
+ VLOG("StatsCallbackPuller::pull succeeded for %d", mTagId);
+ return *pullSuccess;
+ }
}
- VLOG("StatsCallbackPuller::pull succeeded for %d", mTagId);
- return true;
}
} // namespace statsd
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.h b/cmds/statsd/src/external/StatsCallbackPuller.h
index 03e78be4d474..e82e8bb532be 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.h
+++ b/cmds/statsd/src/external/StatsCallbackPuller.h
@@ -16,21 +16,29 @@
#pragma once
-#include <android/os/IStatsPullerCallback.h>
-
+#include <aidl/android/os/IPullAtomCallback.h>
#include "StatsPuller.h"
+using aidl::android::os::IPullAtomCallback;
+using std::shared_ptr;
+
namespace android {
namespace os {
namespace statsd {
class StatsCallbackPuller : public StatsPuller {
public:
- explicit StatsCallbackPuller(int tagId, const sp<IStatsPullerCallback>& callback);
+ explicit StatsCallbackPuller(int tagId, const shared_ptr<IPullAtomCallback>& callback,
+ const int64_t coolDownNs, const int64_t timeoutNs,
+ const std::vector<int> additiveFields);
private:
- bool PullInternal(vector<std::shared_ptr<LogEvent> >* data) override;
- const sp<IStatsPullerCallback> mCallback;
+ bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+ const shared_ptr<IPullAtomCallback> mCallback;
+
+ FRIEND_TEST(StatsCallbackPullerTest, PullFail);
+ FRIEND_TEST(StatsCallbackPullerTest, PullSuccess);
+ FRIEND_TEST(StatsCallbackPullerTest, PullTimeout);
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
deleted file mode 100644
index f37d2bedf8c2..000000000000
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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.
- */
-
-#define DEBUG false
-#include "Log.h"
-
-#include <android/os/IStatsCompanionService.h>
-#include <binder/IPCThreadState.h>
-#include <private/android_filesystem_config.h>
-#include "../stats_log_util.h"
-#include "../statscompanion_util.h"
-#include "StatsCompanionServicePuller.h"
-
-using namespace android;
-using namespace android::base;
-using namespace android::binder;
-using namespace android::os;
-using std::make_shared;
-using std::shared_ptr;
-using std::vector;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// The reading and parsing are implemented in Java. It is not difficult to port over. But for now
-// let StatsCompanionService handle that and send the data back.
-StatsCompanionServicePuller::StatsCompanionServicePuller(int tagId) : StatsPuller(tagId) {
-}
-
-void StatsCompanionServicePuller::SetStatsCompanionService(
- sp<IStatsCompanionService> statsCompanionService) {
- AutoMutex _l(mStatsCompanionServiceLock);
- sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
- mStatsCompanionService = statsCompanionService;
-}
-
-bool StatsCompanionServicePuller::PullInternal(vector<shared_ptr<LogEvent> >* data) {
- sp<IStatsCompanionService> statsCompanionServiceCopy = mStatsCompanionService;
- if (statsCompanionServiceCopy != nullptr) {
- vector<StatsLogEventWrapper> returned_value;
- Status status = statsCompanionServiceCopy->pullData(mTagId, &returned_value);
- if (!status.isOk()) {
- ALOGW("StatsCompanionServicePuller::pull failed for %d", mTagId);
- StatsdStats::getInstance().noteStatsCompanionPullFailed(mTagId);
- if (status.exceptionCode() == Status::Exception::EX_TRANSACTION_FAILED) {
- StatsdStats::getInstance().noteStatsCompanionPullBinderTransactionFailed(mTagId);
- }
- return false;
- }
- data->clear();
- for (const StatsLogEventWrapper& it : returned_value) {
- LogEvent::createLogEvents(it, *data);
- }
- VLOG("StatsCompanionServicePuller::pull succeeded for %d", mTagId);
- return true;
- } else {
- ALOGW("statsCompanion not found!");
- return false;
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.h b/cmds/statsd/src/external/StatsCompanionServicePuller.h
deleted file mode 100644
index 2e133207f01d..000000000000
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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 <utils/String16.h>
-#include "StatsPuller.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class StatsCompanionServicePuller : public StatsPuller {
-public:
- explicit StatsCompanionServicePuller(int tagId);
-
- void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) override;
-
-private:
- Mutex mStatsCompanionServiceLock;
- sp<IStatsCompanionService> mStatsCompanionService = nullptr;
- bool PullInternal(vector<std::shared_ptr<LogEvent> >* data) override;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index 9552c0a5e35e..bb5d0a6bab58 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -32,50 +32,64 @@ using std::lock_guard;
sp<UidMap> StatsPuller::mUidMap = nullptr;
void StatsPuller::SetUidMap(const sp<UidMap>& uidMap) { mUidMap = uidMap; }
-StatsPuller::StatsPuller(const int tagId)
- : mTagId(tagId), mLastPullTimeNs(0) {
+StatsPuller::StatsPuller(const int tagId, const int64_t coolDownNs, const int64_t pullTimeoutNs,
+ const std::vector<int> additiveFields)
+ : mTagId(tagId),
+ mPullTimeoutNs(pullTimeoutNs),
+ mCoolDownNs(coolDownNs),
+ mAdditiveFields(additiveFields),
+ mLastPullTimeNs(0),
+ mLastEventTimeNs(0) {
}
-bool StatsPuller::Pull(std::vector<std::shared_ptr<LogEvent>>* data) {
+bool StatsPuller::Pull(const int64_t eventTimeNs, std::vector<std::shared_ptr<LogEvent>>* data) {
lock_guard<std::mutex> lock(mLock);
- int64_t elapsedTimeNs = getElapsedRealtimeNs();
+ const int64_t elapsedTimeNs = getElapsedRealtimeNs();
+ const int64_t systemUptimeMillis = getSystemUptimeMillis();
StatsdStats::getInstance().notePull(mTagId);
- const bool shouldUseCache = elapsedTimeNs - mLastPullTimeNs <
- StatsPullerManager::kAllPullAtomInfo.at(mTagId).coolDownNs;
+ const bool shouldUseCache =
+ (mLastEventTimeNs == eventTimeNs) || (elapsedTimeNs - mLastPullTimeNs < mCoolDownNs);
if (shouldUseCache) {
if (mHasGoodData) {
(*data) = mCachedData;
StatsdStats::getInstance().notePullFromCache(mTagId);
+
}
return mHasGoodData;
}
-
if (mLastPullTimeNs > 0) {
StatsdStats::getInstance().updateMinPullIntervalSec(
mTagId, (elapsedTimeNs - mLastPullTimeNs) / NS_PER_SEC);
}
mCachedData.clear();
mLastPullTimeNs = elapsedTimeNs;
+ mLastEventTimeNs = eventTimeNs;
mHasGoodData = PullInternal(&mCachedData);
if (!mHasGoodData) {
return mHasGoodData;
}
- const int64_t pullDurationNs = getElapsedRealtimeNs() - elapsedTimeNs;
- StatsdStats::getInstance().notePullTime(mTagId, pullDurationNs);
- const bool pullTimeOut =
- pullDurationNs > StatsPullerManager::kAllPullAtomInfo.at(mTagId).pullTimeoutNs;
+ const int64_t pullElapsedDurationNs = getElapsedRealtimeNs() - elapsedTimeNs;
+ const int64_t pullSystemUptimeDurationMillis = getSystemUptimeMillis() - systemUptimeMillis;
+ StatsdStats::getInstance().notePullTime(mTagId, pullElapsedDurationNs);
+ const bool pullTimeOut = pullElapsedDurationNs > mPullTimeoutNs;
if (pullTimeOut) {
// Something went wrong. Discard the data.
- clearCacheLocked();
+ mCachedData.clear();
mHasGoodData = false;
- StatsdStats::getInstance().notePullTimeout(mTagId);
+ StatsdStats::getInstance().notePullTimeout(
+ mTagId, pullSystemUptimeDurationMillis, NanoToMillis(pullElapsedDurationNs));
ALOGW("Pull for atom %d exceeds timeout %lld nano seconds.", mTagId,
- (long long)pullDurationNs);
+ (long long)pullElapsedDurationNs);
return mHasGoodData;
}
if (mCachedData.size() > 0) {
- mapAndMergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId);
+ mapAndMergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId, mAdditiveFields);
+ }
+
+ if (mCachedData.empty()) {
+ VLOG("Data pulled is empty");
+ StatsdStats::getInstance().noteEmptyData(mTagId);
}
(*data) = mCachedData;
@@ -95,12 +109,12 @@ int StatsPuller::clearCacheLocked() {
int ret = mCachedData.size();
mCachedData.clear();
mLastPullTimeNs = 0;
+ mLastEventTimeNs = 0;
return ret;
}
int StatsPuller::ClearCacheIfNecessary(int64_t timestampNs) {
- if (timestampNs - mLastPullTimeNs >
- StatsPullerManager::kAllPullAtomInfo.at(mTagId).coolDownNs) {
+ if (timestampNs - mLastPullTimeNs > mCoolDownNs) {
return clearCache();
} else {
return 0;
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index c83c4f8536ae..470d15e6fbd1 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -16,7 +16,7 @@
#pragma once
-#include <android/os/IStatsCompanionService.h>
+#include <aidl/android/os/IStatsCompanionService.h>
#include <utils/RefBase.h>
#include <mutex>
#include <vector>
@@ -26,13 +26,19 @@
#include "logd/LogEvent.h"
#include "puller_util.h"
+using aidl::android::os::IStatsCompanionService;
+using std::shared_ptr;
+
namespace android {
namespace os {
namespace statsd {
class StatsPuller : public virtual RefBase {
public:
- explicit StatsPuller(const int tagId);
+ explicit StatsPuller(const int tagId,
+ const int64_t coolDownNs = NS_PER_SEC,
+ const int64_t pullTimeoutNs = StatsdStats::kPullMaxDelayNs,
+ const std::vector<int> additiveFields = std::vector<int>());
virtual ~StatsPuller() {}
@@ -45,7 +51,7 @@ public:
// 2) pull takes longer than mPullTimeoutNs (intrinsic to puller)
// If a metric wants to make any change to the data, like timestamps, it
// should make a copy as this data may be shared with multiple metrics.
- bool Pull(std::vector<std::shared_ptr<LogEvent>>* data);
+ bool Pull(const int64_t eventTimeNs, std::vector<std::shared_ptr<LogEvent>>* data);
// Clear cache immediately
int ForceClearCache();
@@ -55,11 +61,18 @@ public:
static void SetUidMap(const sp<UidMap>& uidMap);
- virtual void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService){};
+ virtual void SetStatsCompanionService(
+ shared_ptr<IStatsCompanionService> statsCompanionService) {};
protected:
const int mTagId;
+ // Max time allowed to pull this atom.
+ // We cannot reliably kill a pull thread. So we don't terminate the puller.
+ // The data is discarded if the pull takes longer than this and mHasGoodData
+ // marked as false.
+ const int64_t mPullTimeoutNs = StatsdStats::kPullMaxDelayNs;
+
private:
mutable std::mutex mLock;
@@ -68,8 +81,24 @@ private:
bool mHasGoodData = false;
+ // Minimum time before this puller does actual pull again.
+ // Pullers can cause significant impact to system health and battery.
+ // So that we don't pull too frequently.
+ // If a pull request comes before cooldown, a cached version from previous pull
+ // will be returned.
+ const int64_t mCoolDownNs = 1 * NS_PER_SEC;
+
+ // The field numbers of the fields that need to be summed when merging
+ // isolated uid with host uid.
+ const std::vector<int> mAdditiveFields;
+
int64_t mLastPullTimeNs;
+ // All pulls happen due to an event (app upgrade, bucket boundary, condition change, etc).
+ // If multiple pulls need to be done at the same event time, we will always use the cache after
+ // the first pull.
+ int64_t mLastEventTimeNs;
+
// Cache of data from last pull. If next request comes before cool down finishes,
// cached data will be returned.
// Cached data is cleared when
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 0b0e4f6ee230..8a9ec7456e55 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -17,26 +17,22 @@
#define DEBUG false
#include "Log.h"
-#include <android/os/IStatsCompanionService.h>
-#include <android/os/IStatsPullerCallback.h>
+#include "StatsPullerManager.h"
+
#include <cutils/log.h>
#include <math.h>
#include <stdint.h>
+
#include <algorithm>
+#include <iostream>
+
#include "../StatsService.h"
#include "../logd/LogEvent.h"
#include "../stats_log_util.h"
#include "../statscompanion_util.h"
-#include "CarStatsPuller.h"
-#include "GpuStatsPuller.h"
-#include "PowerStatsPuller.h"
-#include "ResourceHealthManagerPuller.h"
#include "StatsCallbackPuller.h"
-#include "StatsCompanionServicePuller.h"
-#include "StatsPullerManager.h"
-#include "SubsystemSleepStatePuller.h"
#include "TrainInfoPuller.h"
-#include "statslog.h"
+#include "statslog_statsd.h"
using std::shared_ptr;
using std::vector;
@@ -45,254 +41,132 @@ namespace android {
namespace os {
namespace statsd {
+// Stores the puller as a wp to avoid holding a reference in case it is unregistered and
+// pullAtomCallbackDied is never called.
+struct PullAtomCallbackDeathCookie {
+ PullAtomCallbackDeathCookie(const wp<StatsPullerManager>& pullerManager,
+ const PullerKey& pullerKey, const wp<StatsPuller>& puller) :
+ mPullerManager(pullerManager), mPullerKey(pullerKey), mPuller(puller) {
+ }
+
+ wp<StatsPullerManager> mPullerManager;
+ PullerKey mPullerKey;
+ wp<StatsPuller> mPuller;
+};
+
+void StatsPullerManager::pullAtomCallbackDied(void* cookie) {
+ PullAtomCallbackDeathCookie* cookie_ = static_cast<PullAtomCallbackDeathCookie*>(cookie);
+ sp<StatsPullerManager> thiz = cookie_->mPullerManager.promote();
+ if (!thiz) {
+ return;
+ }
+
+ const PullerKey& pullerKey = cookie_->mPullerKey;
+ wp<StatsPuller> puller = cookie_->mPuller;
+
+ // Erase the mapping from the puller key to the puller if the mapping still exists.
+ // Note that we are removing the StatsPuller object, which internally holds the binder
+ // IPullAtomCallback. However, each new registration creates a new StatsPuller, so this works.
+ lock_guard<mutex> lock(thiz->mLock);
+ const auto& it = thiz->kAllPullAtomInfo.find(pullerKey);
+ if (it != thiz->kAllPullAtomInfo.end() && puller != nullptr && puller == it->second) {
+ StatsdStats::getInstance().notePullerCallbackRegistrationChanged(pullerKey.atomTag,
+ /*registered=*/false);
+ thiz->kAllPullAtomInfo.erase(pullerKey);
+ }
+ // The death recipient corresponding to this specific IPullAtomCallback can never
+ // be triggered again, so free up resources.
+ delete cookie_;
+}
+
// Values smaller than this may require to update the alarm.
const int64_t NO_ALARM_UPDATE = INT64_MAX;
-std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
- // wifi_bytes_transfer
- {android::util::WIFI_BYTES_TRANSFER,
- {.additiveFields = {2, 3, 4, 5},
- .puller = new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER)}},
- // wifi_bytes_transfer_by_fg_bg
- {android::util::WIFI_BYTES_TRANSFER_BY_FG_BG,
- {.additiveFields = {3, 4, 5, 6},
- .puller = new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)}},
- // mobile_bytes_transfer
- {android::util::MOBILE_BYTES_TRANSFER,
- {.additiveFields = {2, 3, 4, 5},
- .puller = new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER)}},
- // mobile_bytes_transfer_by_fg_bg
- {android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG,
- {.additiveFields = {3, 4, 5, 6},
- .puller =
- new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}},
- // bluetooth_bytes_transfer
- {android::util::BLUETOOTH_BYTES_TRANSFER,
- {.additiveFields = {2, 3},
- .puller = new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}},
- // kernel_wakelock
- {android::util::KERNEL_WAKELOCK,
- {.puller = new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
- // subsystem_sleep_state
- {android::util::SUBSYSTEM_SLEEP_STATE, {.puller = new SubsystemSleepStatePuller()}},
- // on_device_power_measurement
- {android::util::ON_DEVICE_POWER_MEASUREMENT, {.puller = new PowerStatsPuller()}},
- // cpu_time_per_freq
- {android::util::CPU_TIME_PER_FREQ,
- {.additiveFields = {3},
- .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
- // cpu_time_per_uid
- {android::util::CPU_TIME_PER_UID,
- {.additiveFields = {2, 3},
- .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID)}},
- // cpu_time_per_uid_freq
- // the throttling is 3sec, handled in
- // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
- {android::util::CPU_TIME_PER_UID_FREQ,
- {.additiveFields = {4},
- .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID_FREQ)}},
- // cpu_active_time
- // the throttling is 3sec, handled in
- // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
- {android::util::CPU_ACTIVE_TIME,
- {.additiveFields = {2},
- .puller = new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
- // cpu_cluster_time
- // the throttling is 3sec, handled in
- // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
- {android::util::CPU_CLUSTER_TIME,
- {.additiveFields = {3},
- .puller = new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
- // wifi_activity_energy_info
- {android::util::WIFI_ACTIVITY_INFO,
- {.puller = new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}},
- // modem_activity_info
- {android::util::MODEM_ACTIVITY_INFO,
- {.puller = new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
- // bluetooth_activity_info
- {android::util::BLUETOOTH_ACTIVITY_INFO,
- {.puller = new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
- // system_elapsed_realtime
- {android::util::SYSTEM_ELAPSED_REALTIME,
- {.coolDownNs = NS_PER_SEC,
- .puller = new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME),
- .pullTimeoutNs = NS_PER_SEC / 2,
- }},
- // system_uptime
- {android::util::SYSTEM_UPTIME,
- {.puller = new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
- // remaining_battery_capacity
- {android::util::REMAINING_BATTERY_CAPACITY,
- {.puller = new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}},
- // full_battery_capacity
- {android::util::FULL_BATTERY_CAPACITY,
- {.puller = new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
- // battery_voltage
- {android::util::BATTERY_VOLTAGE,
- {.puller = new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
- // battery_level
- {android::util::BATTERY_LEVEL,
- {.puller = new ResourceHealthManagerPuller(android::util::BATTERY_LEVEL)}},
- // battery_cycle_count
- {android::util::BATTERY_CYCLE_COUNT,
- {.puller = new ResourceHealthManagerPuller(android::util::BATTERY_CYCLE_COUNT)}},
- // process_memory_state
- {android::util::PROCESS_MEMORY_STATE,
- {.additiveFields = {4, 5, 6, 7, 8, 9},
- .puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
- // native_process_memory_state
- {android::util::NATIVE_PROCESS_MEMORY_STATE,
- {.additiveFields = {3, 4, 5, 6, 8},
- .puller = new StatsCompanionServicePuller(android::util::NATIVE_PROCESS_MEMORY_STATE)}},
- // process_memory_high_water_mark
- {android::util::PROCESS_MEMORY_HIGH_WATER_MARK,
- {.additiveFields = {3},
- .puller =
- new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}},
- // system_ion_heap_size
- {android::util::SYSTEM_ION_HEAP_SIZE,
- {.puller = new StatsCompanionServicePuller(android::util::SYSTEM_ION_HEAP_SIZE)}},
- // process_system_ion_heap_size
- {android::util::PROCESS_SYSTEM_ION_HEAP_SIZE,
- {.puller = new StatsCompanionServicePuller(android::util::PROCESS_SYSTEM_ION_HEAP_SIZE)}},
- // temperature
- {android::util::TEMPERATURE,
- {.puller = new StatsCompanionServicePuller(android::util::TEMPERATURE)}},
- // cooling_device
- {android::util::COOLING_DEVICE,
- {.puller = new StatsCompanionServicePuller(android::util::COOLING_DEVICE)}},
- // binder_calls
- {android::util::BINDER_CALLS,
- {.additiveFields = {4, 5, 6, 8, 12},
- .puller = new StatsCompanionServicePuller(android::util::BINDER_CALLS)}},
- // binder_calls_exceptions
- {android::util::BINDER_CALLS_EXCEPTIONS,
- {.puller = new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}},
- // looper_stats
- {android::util::LOOPER_STATS,
- {.additiveFields = {5, 6, 7, 8, 9},
- .puller = new StatsCompanionServicePuller(android::util::LOOPER_STATS)}},
- // Disk Stats
- {android::util::DISK_STATS,
- {.puller = new StatsCompanionServicePuller(android::util::DISK_STATS)}},
- // Directory usage
- {android::util::DIRECTORY_USAGE,
- {.puller = new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
- // Size of app's code, data, and cache
- {android::util::APP_SIZE,
- {.puller = new StatsCompanionServicePuller(android::util::APP_SIZE)}},
- // Size of specific categories of files. Eg. Music.
- {android::util::CATEGORY_SIZE,
- {.puller = new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
- // Number of fingerprints enrolled for each user.
- {android::util::NUM_FINGERPRINTS_ENROLLED,
- {.puller = new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS_ENROLLED)}},
- // Number of faces enrolled for each user.
- {android::util::NUM_FACES_ENROLLED,
- {.puller = new StatsCompanionServicePuller(android::util::NUM_FACES_ENROLLED)}},
- // ProcStats.
- {android::util::PROC_STATS,
- {.puller = new StatsCompanionServicePuller(android::util::PROC_STATS)}},
- // ProcStatsPkgProc.
- {android::util::PROC_STATS_PKG_PROC,
- {.puller = new StatsCompanionServicePuller(android::util::PROC_STATS_PKG_PROC)}},
- // Disk I/O stats per uid.
- {android::util::DISK_IO,
- {.additiveFields = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
- .coolDownNs = 3 * NS_PER_SEC,
- .puller = new StatsCompanionServicePuller(android::util::DISK_IO)}},
- // PowerProfile constants for power model calculations.
- {android::util::POWER_PROFILE,
- {.puller = new StatsCompanionServicePuller(android::util::POWER_PROFILE)}},
- // Process cpu stats. Min cool-down is 5 sec, inline with what AcitivityManagerService uses.
- {android::util::PROCESS_CPU_TIME,
- {.coolDownNs = 5 * NS_PER_SEC /* min cool-down in seconds*/,
- .puller = new StatsCompanionServicePuller(android::util::PROCESS_CPU_TIME)}},
- {android::util::CPU_TIME_PER_THREAD_FREQ,
- {.additiveFields = {7, 9, 11, 13, 15, 17, 19, 21},
- .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_THREAD_FREQ)}},
- // DeviceCalculatedPowerUse.
- {android::util::DEVICE_CALCULATED_POWER_USE,
- {.puller = new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_USE)}},
- // DeviceCalculatedPowerBlameUid.
- {android::util::DEVICE_CALCULATED_POWER_BLAME_UID,
- {.puller = new StatsCompanionServicePuller(
- android::util::DEVICE_CALCULATED_POWER_BLAME_UID)}},
- // DeviceCalculatedPowerBlameOther.
- {android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER,
- {.puller = new StatsCompanionServicePuller(
- android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER)}},
- // DebugElapsedClock.
- {android::util::DEBUG_ELAPSED_CLOCK,
- {.additiveFields = {1, 2, 3, 4},
- .puller = new StatsCompanionServicePuller(android::util::DEBUG_ELAPSED_CLOCK)}},
- // DebugFailingElapsedClock.
- {android::util::DEBUG_FAILING_ELAPSED_CLOCK,
- {.additiveFields = {1, 2, 3, 4},
- .puller = new StatsCompanionServicePuller(android::util::DEBUG_FAILING_ELAPSED_CLOCK)}},
- // BuildInformation.
- {android::util::BUILD_INFORMATION,
- {.puller = new StatsCompanionServicePuller(android::util::BUILD_INFORMATION)}},
- // RoleHolder.
- {android::util::ROLE_HOLDER,
- {.puller = new StatsCompanionServicePuller(android::util::ROLE_HOLDER)}},
- // PermissionState.
- {android::util::DANGEROUS_PERMISSION_STATE,
- {.puller = new StatsCompanionServicePuller(android::util::DANGEROUS_PERMISSION_STATE)}},
- // TrainInfo.
- {android::util::TRAIN_INFO, {.puller = new TrainInfoPuller()}},
- // TimeZoneDataInfo.
- {android::util::TIME_ZONE_DATA_INFO,
- {.puller = new StatsCompanionServicePuller(android::util::TIME_ZONE_DATA_INFO)}},
- // ExternalStorageInfo
- {android::util::EXTERNAL_STORAGE_INFO,
- {.puller = new StatsCompanionServicePuller(android::util::EXTERNAL_STORAGE_INFO)}},
- // GpuStatsGlobalInfo
- {android::util::GPU_STATS_GLOBAL_INFO,
- {.puller = new GpuStatsPuller(android::util::GPU_STATS_GLOBAL_INFO)}},
- // GpuStatsAppInfo
- {android::util::GPU_STATS_APP_INFO,
- {.puller = new GpuStatsPuller(android::util::GPU_STATS_APP_INFO)}},
- // AppsOnExternalStorageInfo
- {android::util::APPS_ON_EXTERNAL_STORAGE_INFO,
- {.puller = new StatsCompanionServicePuller(android::util::APPS_ON_EXTERNAL_STORAGE_INFO)}},
- // Face Settings
- {android::util::FACE_SETTINGS,
- {.puller = new StatsCompanionServicePuller(android::util::FACE_SETTINGS)}},
- // App ops
- {android::util::APP_OPS,
- {.puller = new StatsCompanionServicePuller(android::util::APP_OPS)}},
- // VmsClientStats
- {android::util::VMS_CLIENT_STATS,
- {.additiveFields = {5, 6, 7, 8, 9, 10},
- .puller = new CarStatsPuller(android::util::VMS_CLIENT_STATS)}},
- // NotiifcationRemoteViews.
- {android::util::NOTIFICATION_REMOTE_VIEWS,
- {.puller = new StatsCompanionServicePuller(android::util::NOTIFICATION_REMOTE_VIEWS)}},
-};
+StatsPullerManager::StatsPullerManager()
+ : kAllPullAtomInfo({
+ // TrainInfo.
+ {{.atomTag = util::TRAIN_INFO, .uid = AID_STATSD}, new TrainInfoPuller()},
+ }),
+ mNextPullTimeNs(NO_ALARM_UPDATE),
+ mPullAtomCallbackDeathRecipient(AIBinder_DeathRecipient_new(pullAtomCallbackDied)) {
+}
-StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
+bool StatsPullerManager::Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
+ vector<shared_ptr<LogEvent>>* data, bool useUids) {
+ std::lock_guard<std::mutex> _l(mLock);
+ return PullLocked(tagId, configKey, eventTimeNs, data, useUids);
}
-bool StatsPullerManager::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) {
- VLOG("Initiating pulling %d", tagId);
+bool StatsPullerManager::Pull(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool useUids) {
+ std::lock_guard<std::mutex> _l(mLock);
+ return PullLocked(tagId, uids, eventTimeNs, data, useUids);
+}
+
+bool StatsPullerManager::PullLocked(int tagId, const ConfigKey& configKey,
+ const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data,
+ bool useUids) {
+ vector<int32_t> uids;
+ if (useUids) {
+ auto uidProviderIt = mPullUidProviders.find(configKey);
+ if (uidProviderIt == mPullUidProviders.end()) {
+ ALOGE("Error pulling tag %d. No pull uid provider for config key %s", tagId,
+ configKey.ToString().c_str());
+ StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
+ return false;
+ }
+ sp<PullUidProvider> pullUidProvider = uidProviderIt->second.promote();
+ if (pullUidProvider == nullptr) {
+ ALOGE("Error pulling tag %d, pull uid provider for config %s is gone.", tagId,
+ configKey.ToString().c_str());
+ StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
+ return false;
+ }
+ uids = pullUidProvider->getPullAtomUids(tagId);
+ }
+ return PullLocked(tagId, uids, eventTimeNs, data, useUids);
+}
- if (kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end()) {
- bool ret = kAllPullAtomInfo.find(tagId)->second.puller->Pull(data);
- VLOG("pulled %d items", (int)data->size());
- if (!ret) {
- StatsdStats::getInstance().notePullFailed(tagId);
+bool StatsPullerManager::PullLocked(int tagId, const vector<int32_t>& uids,
+ const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data,
+ bool useUids) {
+ VLOG("Initiating pulling %d", tagId);
+ if (useUids) {
+ for (int32_t uid : uids) {
+ PullerKey key = {.atomTag = tagId, .uid = uid};
+ auto pullerIt = kAllPullAtomInfo.find(key);
+ if (pullerIt != kAllPullAtomInfo.end()) {
+ bool ret = pullerIt->second->Pull(eventTimeNs, data);
+ VLOG("pulled %zu items", data->size());
+ if (!ret) {
+ StatsdStats::getInstance().notePullFailed(tagId);
+ }
+ return ret;
+ }
}
- return ret;
+ StatsdStats::getInstance().notePullerNotFound(tagId);
+ ALOGW("StatsPullerManager: Unknown tagId %d", tagId);
+ return false; // Return early since we don't know what to pull.
} else {
- VLOG("Unknown tagId %d", tagId);
+ PullerKey key = {.atomTag = tagId, .uid = -1};
+ auto pullerIt = kAllPullAtomInfo.find(key);
+ if (pullerIt != kAllPullAtomInfo.end()) {
+ bool ret = pullerIt->second->Pull(eventTimeNs, data);
+ VLOG("pulled %zu items", data->size());
+ if (!ret) {
+ StatsdStats::getInstance().notePullFailed(tagId);
+ }
+ return ret;
+ }
+ ALOGW("StatsPullerManager: Unknown tagId %d", tagId);
return false; // Return early since we don't know what to pull.
}
}
bool StatsPullerManager::PullerForMatcherExists(int tagId) const {
- // Vendor pulled atoms might be registered after we parse the config.
- return isVendorPulledAtom(tagId) || kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end();
+ // Pulled atoms might be registered after we parse the config, so just make sure the id is in
+ // an appropriate range.
+ return isVendorPulledAtom(tagId) || isPulledAtom(tagId);
}
void StatsPullerManager::updateAlarmLocked() {
@@ -301,9 +175,9 @@ void StatsPullerManager::updateAlarmLocked() {
return;
}
- sp<IStatsCompanionService> statsCompanionServiceCopy = mStatsCompanionService;
- if (statsCompanionServiceCopy != nullptr) {
- statsCompanionServiceCopy->setPullingAlarm(mNextPullTimeNs / 1000000);
+ // TODO(b/151045771): do not hold a lock while making a binder call
+ if (mStatsCompanionService != nullptr) {
+ mStatsCompanionService->setPullingAlarm(mNextPullTimeNs / 1000000);
} else {
VLOG("StatsCompanionService not available. Alarm not set.");
}
@@ -311,22 +185,23 @@ void StatsPullerManager::updateAlarmLocked() {
}
void StatsPullerManager::SetStatsCompanionService(
- sp<IStatsCompanionService> statsCompanionService) {
- AutoMutex _l(mLock);
- sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
+ shared_ptr<IStatsCompanionService> statsCompanionService) {
+ std::lock_guard<std::mutex> _l(mLock);
+ shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService;
mStatsCompanionService = statsCompanionService;
for (const auto& pulledAtom : kAllPullAtomInfo) {
- pulledAtom.second.puller->SetStatsCompanionService(statsCompanionService);
+ pulledAtom.second->SetStatsCompanionService(statsCompanionService);
}
if (mStatsCompanionService != nullptr) {
updateAlarmLocked();
}
}
-void StatsPullerManager::RegisterReceiver(int tagId, wp<PullDataReceiver> receiver,
- int64_t nextPullTimeNs, int64_t intervalNs) {
- AutoMutex _l(mLock);
- auto& receivers = mReceivers[tagId];
+void StatsPullerManager::RegisterReceiver(int tagId, const ConfigKey& configKey,
+ wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
+ int64_t intervalNs) {
+ std::lock_guard<std::mutex> _l(mLock);
+ auto& receivers = mReceivers[{.atomTag = tagId, .configKey = configKey}];
for (auto it = receivers.begin(); it != receivers.end(); it++) {
if (it->receiver == receiver) {
VLOG("Receiver already registered of %d", (int)receivers.size());
@@ -358,13 +233,15 @@ void StatsPullerManager::RegisterReceiver(int tagId, wp<PullDataReceiver> receiv
VLOG("Puller for tagId %d registered of %d", tagId, (int)receivers.size());
}
-void StatsPullerManager::UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver) {
- AutoMutex _l(mLock);
- if (mReceivers.find(tagId) == mReceivers.end()) {
+void StatsPullerManager::UnRegisterReceiver(int tagId, const ConfigKey& configKey,
+ wp<PullDataReceiver> receiver) {
+ std::lock_guard<std::mutex> _l(mLock);
+ auto receiversIt = mReceivers.find({.atomTag = tagId, .configKey = configKey});
+ if (receiversIt == mReceivers.end()) {
VLOG("Unknown pull code or no receivers: %d", tagId);
return;
}
- auto& receivers = mReceivers.find(tagId)->second;
+ std::list<ReceiverInfo>& receivers = receiversIt->second;
for (auto it = receivers.begin(); it != receivers.end(); it++) {
if (receiver == it->receiver) {
receivers.erase(it);
@@ -374,16 +251,30 @@ void StatsPullerManager::UnRegisterReceiver(int tagId, wp<PullDataReceiver> rece
}
}
+void StatsPullerManager::RegisterPullUidProvider(const ConfigKey& configKey,
+ wp<PullUidProvider> provider) {
+ std::lock_guard<std::mutex> _l(mLock);
+ mPullUidProviders[configKey] = provider;
+}
+
+void StatsPullerManager::UnregisterPullUidProvider(const ConfigKey& configKey,
+ wp<PullUidProvider> provider) {
+ std::lock_guard<std::mutex> _l(mLock);
+ const auto& it = mPullUidProviders.find(configKey);
+ if (it != mPullUidProviders.end() && it->second == provider) {
+ mPullUidProviders.erase(it);
+ }
+}
+
void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) {
- AutoMutex _l(mLock);
+ std::lock_guard<std::mutex> _l(mLock);
int64_t wallClockNs = getWallClockNs();
int64_t minNextPullTimeNs = NO_ALARM_UPDATE;
- vector<pair<int, vector<ReceiverInfo*>>> needToPull =
- vector<pair<int, vector<ReceiverInfo*>>>();
+ vector<pair<const ReceiverKey*, vector<ReceiverInfo*>>> needToPull;
for (auto& pair : mReceivers) {
- vector<ReceiverInfo*> receivers = vector<ReceiverInfo*>();
+ vector<ReceiverInfo*> receivers;
if (pair.second.size() != 0) {
for (ReceiverInfo& receiverInfo : pair.second) {
if (receiverInfo.nextPullTimeNs <= elapsedTimeNs) {
@@ -395,18 +286,15 @@ void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) {
}
}
if (receivers.size() > 0) {
- needToPull.push_back(make_pair(pair.first, receivers));
+ needToPull.push_back(make_pair(&pair.first, receivers));
}
}
}
-
for (const auto& pullInfo : needToPull) {
vector<shared_ptr<LogEvent>> data;
- bool pullSuccess = Pull(pullInfo.first, &data);
- if (pullSuccess) {
- StatsdStats::getInstance().notePullDelay(
- pullInfo.first, getElapsedRealtimeNs() - elapsedTimeNs);
- } else {
+ bool pullSuccess = PullLocked(pullInfo.first->atomTag, pullInfo.first->configKey,
+ elapsedTimeNs, &data);
+ if (!pullSuccess) {
VLOG("pull failed at %lld, will try again later", (long long)elapsedTimeNs);
}
@@ -448,7 +336,7 @@ void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) {
int StatsPullerManager::ForceClearPullerCache() {
int totalCleared = 0;
for (const auto& pulledAtom : kAllPullAtomInfo) {
- totalCleared += pulledAtom.second.puller->ForceClearCache();
+ totalCleared += pulledAtom.second->ForceClearCache();
}
return totalCleared;
}
@@ -456,32 +344,45 @@ int StatsPullerManager::ForceClearPullerCache() {
int StatsPullerManager::ClearPullerCacheIfNecessary(int64_t timestampNs) {
int totalCleared = 0;
for (const auto& pulledAtom : kAllPullAtomInfo) {
- totalCleared += pulledAtom.second.puller->ClearCacheIfNecessary(timestampNs);
+ totalCleared += pulledAtom.second->ClearCacheIfNecessary(timestampNs);
}
return totalCleared;
}
-void StatsPullerManager::RegisterPullerCallback(int32_t atomTag,
- const sp<IStatsPullerCallback>& callback) {
- AutoMutex _l(mLock);
- // Platform pullers cannot be changed.
- if (!isVendorPulledAtom(atomTag)) {
- VLOG("RegisterPullerCallback: atom tag %d is not vendor pulled", atomTag);
+void StatsPullerManager::RegisterPullAtomCallback(const int uid, const int32_t atomTag,
+ const int64_t coolDownNs, const int64_t timeoutNs,
+ const vector<int32_t>& additiveFields,
+ const shared_ptr<IPullAtomCallback>& callback,
+ bool useUid) {
+ std::lock_guard<std::mutex> _l(mLock);
+ VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag);
+
+ if (callback == nullptr) {
+ ALOGW("SetPullAtomCallback called with null callback for atom %d.", atomTag);
return;
}
- VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag);
+
StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/true);
- kAllPullAtomInfo[atomTag] = {.puller = new StatsCallbackPuller(atomTag, callback)};
+ int64_t actualCoolDownNs = coolDownNs < kMinCoolDownNs ? kMinCoolDownNs : coolDownNs;
+ int64_t actualTimeoutNs = timeoutNs > kMaxTimeoutNs ? kMaxTimeoutNs : timeoutNs;
+
+ sp<StatsCallbackPuller> puller = new StatsCallbackPuller(atomTag, callback, actualCoolDownNs,
+ actualTimeoutNs, additiveFields);
+ PullerKey key = {.atomTag = atomTag, .uid = useUid ? uid : -1};
+ AIBinder_linkToDeath(callback->asBinder().get(), mPullAtomCallbackDeathRecipient.get(),
+ new PullAtomCallbackDeathCookie(this, key, puller));
+ kAllPullAtomInfo[key] = puller;
}
-void StatsPullerManager::UnregisterPullerCallback(int32_t atomTag) {
- AutoMutex _l(mLock);
- // Platform pullers cannot be changed.
- if (!isVendorPulledAtom(atomTag)) {
- return;
+void StatsPullerManager::UnregisterPullAtomCallback(const int uid, const int32_t atomTag,
+ bool useUids) {
+ std::lock_guard<std::mutex> _l(mLock);
+ PullerKey key = {.atomTag = atomTag, .uid = useUids ? uid : -1};
+ if (kAllPullAtomInfo.find(key) != kAllPullAtomInfo.end()) {
+ StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag,
+ /*registered=*/false);
+ kAllPullAtomInfo.erase(key);
}
- StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/false);
- kAllPullAtomInfo.erase(atomTag);
}
} // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 6791f6656f09..194a0f5edba8 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -16,40 +16,48 @@
#pragma once
-#include <android/os/IStatsCompanionService.h>
-#include <android/os/IStatsPullerCallback.h>
-#include <binder/IServiceManager.h>
+#include <aidl/android/os/IPullAtomCallback.h>
+#include <aidl/android/os/IStatsCompanionService.h>
#include <utils/RefBase.h>
-#include <utils/threads.h>
+
#include <list>
#include <vector>
+
#include "PullDataReceiver.h"
+#include "PullUidProvider.h"
#include "StatsPuller.h"
#include "guardrail/StatsdStats.h"
#include "logd/LogEvent.h"
+#include "packages/UidMap.h"
+
+using aidl::android::os::IPullAtomCallback;
+using aidl::android::os::IStatsCompanionService;
+using std::shared_ptr;
namespace android {
namespace os {
namespace statsd {
-typedef struct {
- // The field numbers of the fields that need to be summed when merging
- // isolated uid with host uid.
- std::vector<int> additiveFields;
- // Minimum time before this puller does actual pull again.
- // Pullers can cause significant impact to system health and battery.
- // So that we don't pull too frequently.
- // If a pull request comes before cooldown, a cached version from previous pull
- // will be returned.
- int64_t coolDownNs = 1 * NS_PER_SEC;
- // The actual puller
- sp<StatsPuller> puller;
- // Max time allowed to pull this atom.
- // We cannot reliably kill a pull thread. So we don't terminate the puller.
- // The data is discarded if the pull takes longer than this and mHasGoodData
- // marked as false.
- int64_t pullTimeoutNs = StatsdStats::kPullMaxDelayNs;
-} PullAtomInfo;
+typedef struct PullerKey {
+ // The uid of the process that registers this puller.
+ const int uid = -1;
+ // The atom that this puller is for.
+ const int atomTag;
+
+ bool operator<(const PullerKey& that) const {
+ if (uid < that.uid) {
+ return true;
+ }
+ if (uid > that.uid) {
+ return false;
+ }
+ return atomTag < that.atomTag;
+ };
+
+ bool operator==(const PullerKey& that) const {
+ return uid == that.uid && atomTag == that.atomTag;
+ };
+} PullerKey;
class StatsPullerManager : public virtual RefBase {
public:
@@ -58,13 +66,24 @@ public:
virtual ~StatsPullerManager() {
}
+
// Registers a receiver for tagId. It will be pulled on the nextPullTimeNs
// and then every intervalNs thereafter.
- virtual void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
+ virtual void RegisterReceiver(int tagId, const ConfigKey& configKey,
+ wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
int64_t intervalNs);
// Stop listening on a tagId.
- virtual void UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver);
+ virtual void UnRegisterReceiver(int tagId, const ConfigKey& configKey,
+ wp<PullDataReceiver> receiver);
+
+ // Registers a pull uid provider for the config key. When pulling atoms, it will be used to
+ // determine which uids to pull from.
+ virtual void RegisterPullUidProvider(const ConfigKey& configKey, wp<PullUidProvider> provider);
+
+ // Unregister a pull uid provider.
+ virtual void UnregisterPullUidProvider(const ConfigKey& configKey,
+ wp<PullUidProvider> provider);
// Verify if we know how to pull for this matcher
bool PullerForMatcherExists(int tagId) const;
@@ -78,9 +97,16 @@ public:
// Returns false when
// 1) the pull fails
// 2) pull takes longer than mPullTimeoutNs (intrinsic to puller)
+ // 3) Either a PullUidProvider was not registered for the config, or the there was no puller
+ // registered for any of the uids for this atom.
// If the metric wants to make any change to the data, like timestamps, they
// should make a copy as this data may be shared with multiple metrics.
- virtual bool Pull(int tagId, vector<std::shared_ptr<LogEvent>>* data);
+ virtual bool Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool useUids = true);
+
+ // Same as above, but directly specify the allowed uids to pull from.
+ virtual bool Pull(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool useUids = true);
// Clear pull data cache immediately.
int ForceClearPullerCache();
@@ -88,17 +114,31 @@ public:
// Clear pull data cache if it is beyond respective cool down time.
int ClearPullerCacheIfNecessary(int64_t timestampNs);
- void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService);
+ void SetStatsCompanionService(shared_ptr<IStatsCompanionService> statsCompanionService);
- void RegisterPullerCallback(int32_t atomTag,
- const sp<IStatsPullerCallback>& callback);
+ void RegisterPullAtomCallback(const int uid, const int32_t atomTag, const int64_t coolDownNs,
+ const int64_t timeoutNs, const vector<int32_t>& additiveFields,
+ const shared_ptr<IPullAtomCallback>& callback,
+ bool useUid = true);
- void UnregisterPullerCallback(int32_t atomTag);
+ void UnregisterPullAtomCallback(const int uid, const int32_t atomTag, bool useUids = true);
- static std::map<int, PullAtomInfo> kAllPullAtomInfo;
+ std::map<const PullerKey, sp<StatsPuller>> kAllPullAtomInfo;
private:
- sp<IStatsCompanionService> mStatsCompanionService = nullptr;
+ const static int64_t kMinCoolDownNs = NS_PER_SEC;
+ const static int64_t kMaxTimeoutNs = 10 * NS_PER_SEC;
+ shared_ptr<IStatsCompanionService> mStatsCompanionService = nullptr;
+
+ // A struct containing an atom id and a Config Key
+ typedef struct ReceiverKey {
+ const int atomTag;
+ const ConfigKey configKey;
+
+ inline bool operator<(const ReceiverKey& that) const {
+ return atomTag == that.atomTag ? configKey < that.configKey : atomTag < that.atomTag;
+ }
+ } ReceiverKey;
typedef struct {
int64_t nextPullTimeNs;
@@ -106,16 +146,34 @@ private:
wp<PullDataReceiver> receiver;
} ReceiverInfo;
- // mapping from simple matcher tagId to receivers
- std::map<int, std::list<ReceiverInfo>> mReceivers;
+ // mapping from Receiver Key to receivers
+ std::map<ReceiverKey, std::list<ReceiverInfo>> mReceivers;
+
+ // mapping from Config Key to the PullUidProvider for that config
+ std::map<ConfigKey, wp<PullUidProvider>> mPullUidProviders;
+
+ bool PullLocked(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool useUids = true);
+
+ bool PullLocked(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool useUids);
// locks for data receiver and StatsCompanionService changes
- Mutex mLock;
+ std::mutex mLock;
void updateAlarmLocked();
int64_t mNextPullTimeNs;
+ // Death recipient that is triggered when the process holding the IPullAtomCallback has died.
+ ::ndk::ScopedAIBinder_DeathRecipient mPullAtomCallbackDeathRecipient;
+
+ /**
+ * Death recipient callback that is called when a pull atom callback dies.
+ * The cookie is a pointer to a PullAtomCallbackDeathCookie.
+ */
+ static void pullAtomCallbackDied(void* cookie);
+
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents);
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm);
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation);
@@ -123,6 +181,8 @@ private:
FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation);
+
+ FRIEND_TEST(StatsLogProcessorTest, TestPullUidProviderSetOnConfigUpdate);
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
deleted file mode 100644
index f6a4aeaa3f9e..000000000000
--- a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * 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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include <android/hardware/power/1.0/IPower.h>
-#include <android/hardware/power/1.1/IPower.h>
-#include <android/hardware/power/stats/1.0/IPowerStats.h>
-
-#include <fcntl.h>
-#include <hardware/power.h>
-#include <hardware_legacy/power.h>
-#include <inttypes.h>
-#include <semaphore.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include "external/SubsystemSleepStatePuller.h"
-#include "external/StatsPuller.h"
-
-#include "SubsystemSleepStatePuller.h"
-#include "logd/LogEvent.h"
-#include "statslog.h"
-#include "stats_log_util.h"
-
-using android::hardware::hidl_vec;
-using android::hardware::power::V1_0::IPower;
-using android::hardware::power::V1_0::PowerStatePlatformSleepState;
-using android::hardware::power::V1_0::PowerStateVoter;
-using android::hardware::power::V1_1::PowerStateSubsystem;
-using android::hardware::power::V1_1::PowerStateSubsystemSleepState;
-using android::hardware::power::stats::V1_0::PowerEntityInfo;
-using android::hardware::power::stats::V1_0::PowerEntityStateResidencyResult;
-using android::hardware::power::stats::V1_0::PowerEntityStateSpace;
-
-using android::hardware::Return;
-using android::hardware::Void;
-
-using std::make_shared;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-static std::function<bool(vector<shared_ptr<LogEvent>>* data)> gPuller = {};
-
-static sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0 = nullptr;
-static sp<android::hardware::power::V1_1::IPower> gPowerHalV1_1 = nullptr;
-static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0 = nullptr;
-
-static std::unordered_map<uint32_t, std::string> gEntityNames = {};
-static std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> gStateNames = {};
-
-static std::mutex gPowerHalMutex;
-
-// The caller must be holding gPowerHalMutex.
-static void deinitPowerStatsLocked() {
- gPowerHalV1_0 = nullptr;
- gPowerHalV1_1 = nullptr;
- gPowerStatsHalV1_0 = nullptr;
-}
-
-struct SubsystemSleepStatePullerDeathRecipient : virtual public hardware::hidl_death_recipient {
- virtual void serviceDied(uint64_t cookie,
- const wp<android::hidl::base::V1_0::IBase>& who) override {
-
- // The HAL just died. Reset all handles to HAL services.
- std::lock_guard<std::mutex> lock(gPowerHalMutex);
- deinitPowerStatsLocked();
- }
-};
-
-static sp<SubsystemSleepStatePullerDeathRecipient> gDeathRecipient =
- new SubsystemSleepStatePullerDeathRecipient();
-
-SubsystemSleepStatePuller::SubsystemSleepStatePuller() :
- StatsPuller(android::util::SUBSYSTEM_SLEEP_STATE) {
-}
-
-// The caller must be holding gPowerHalMutex.
-static bool checkResultLocked(const Return<void> &ret, const char* function) {
- if (!ret.isOk()) {
- ALOGE("%s failed: requested HAL service not available. Description: %s",
- function, ret.description().c_str());
- if (ret.isDeadObject()) {
- deinitPowerStatsLocked();
- }
- return false;
- }
- return true;
-}
-
-// The caller must be holding gPowerHalMutex.
-// gPowerStatsHalV1_0 must not be null
-static bool initializePowerStats() {
- using android::hardware::power::stats::V1_0::Status;
-
- // Clear out previous content if we are re-initializing
- gEntityNames.clear();
- gStateNames.clear();
-
- Return<void> ret;
- ret = gPowerStatsHalV1_0->getPowerEntityInfo([](auto infos, auto status) {
- if (status != Status::SUCCESS) {
- ALOGE("Error getting power entity info");
- return;
- }
-
- // construct lookup table of powerEntityId to power entity name
- for (auto info : infos) {
- gEntityNames.emplace(info.powerEntityId, info.powerEntityName);
- }
- });
- if (!checkResultLocked(ret, __func__)) {
- return false;
- }
-
- ret = gPowerStatsHalV1_0->getPowerEntityStateInfo({}, [](auto stateSpaces, auto status) {
- if (status != Status::SUCCESS) {
- ALOGE("Error getting state info");
- return;
- }
-
- // construct lookup table of powerEntityId, powerEntityStateId to power entity state name
- for (auto stateSpace : stateSpaces) {
- std::unordered_map<uint32_t, std::string> stateNames = {};
- for (auto state : stateSpace.states) {
- stateNames.emplace(state.powerEntityStateId,
- state.powerEntityStateName);
- }
- gStateNames.emplace(stateSpace.powerEntityId, stateNames);
- }
- });
- if (!checkResultLocked(ret, __func__)) {
- return false;
- }
-
- return (!gEntityNames.empty()) && (!gStateNames.empty());
-}
-
-// The caller must be holding gPowerHalMutex.
-static bool getPowerStatsHalLocked() {
- if(gPowerStatsHalV1_0 == nullptr) {
- gPowerStatsHalV1_0 = android::hardware::power::stats::V1_0::IPowerStats::getService();
- if (gPowerStatsHalV1_0 == nullptr) {
- ALOGE("Unable to get power.stats HAL service.");
- return false;
- }
-
- // Link death recipient to power.stats service handle
- hardware::Return<bool> linked = gPowerStatsHalV1_0->linkToDeath(gDeathRecipient, 0);
- if (!linked.isOk()) {
- ALOGE("Transaction error in linking to power.stats HAL death: %s",
- linked.description().c_str());
- deinitPowerStatsLocked();
- return false;
- } else if (!linked) {
- ALOGW("Unable to link to power.stats HAL death notifications");
- // We should still continue even though linking failed
- }
- return initializePowerStats();
- }
- return true;
-}
-
-// The caller must be holding gPowerHalMutex.
-static bool getIPowerStatsDataLocked(vector<shared_ptr<LogEvent>>* data) {
- using android::hardware::power::stats::V1_0::Status;
-
- if(!getPowerStatsHalLocked()) {
- return false;
- }
-
- int64_t wallClockTimestampNs = getWallClockNs();
- int64_t elapsedTimestampNs = getElapsedRealtimeNs();
-
- // Get power entity state residency data
- bool success = false;
- Return<void> ret = gPowerStatsHalV1_0->getPowerEntityStateResidencyData({},
- [&data, &success, wallClockTimestampNs, elapsedTimestampNs]
- (auto results, auto status) {
- if (status == Status::NOT_SUPPORTED) {
- ALOGW("getPowerEntityStateResidencyData is not supported");
- success = false;
- return;
- }
-
- for(auto result : results) {
- for(auto stateResidency : result.stateResidencyData) {
- auto statePtr = make_shared<LogEvent>(
- android::util::SUBSYSTEM_SLEEP_STATE,
- wallClockTimestampNs, elapsedTimestampNs);
- statePtr->write(gEntityNames.at(result.powerEntityId));
- statePtr->write(gStateNames.at(result.powerEntityId)
- .at(stateResidency.powerEntityStateId));
- statePtr->write(stateResidency.totalStateEntryCount);
- statePtr->write(stateResidency.totalTimeInStateMs);
- statePtr->init();
- data->emplace_back(statePtr);
- }
- }
- success = true;
- });
- // Intentionally not returning early here.
- // bool success determines if this succeeded or not.
- checkResultLocked(ret, __func__);
-
- return success;
-}
-
-// The caller must be holding gPowerHalMutex.
-static bool getPowerHalLocked() {
- if(gPowerHalV1_0 == nullptr) {
- gPowerHalV1_0 = android::hardware::power::V1_0::IPower::getService();
- if(gPowerHalV1_0 == nullptr) {
- ALOGE("Unable to get power HAL service.");
- return false;
- }
- gPowerHalV1_1 = android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
-
- // Link death recipient to power service handle
- hardware::Return<bool> linked = gPowerHalV1_0->linkToDeath(gDeathRecipient, 0);
- if (!linked.isOk()) {
- ALOGE("Transaction error in linking to power HAL death: %s",
- linked.description().c_str());
- gPowerHalV1_0 = nullptr;
- return false;
- } else if (!linked) {
- ALOGW("Unable to link to power. death notifications");
- // We should still continue even though linking failed
- }
- }
- return true;
-}
-
-// The caller must be holding gPowerHalMutex.
-static bool getIPowerDataLocked(vector<shared_ptr<LogEvent>>* data) {
- using android::hardware::power::V1_0::Status;
-
- if(!getPowerHalLocked()) {
- return false;
- }
-
- int64_t wallClockTimestampNs = getWallClockNs();
- int64_t elapsedTimestampNs = getElapsedRealtimeNs();
- Return<void> ret;
- ret = gPowerHalV1_0->getPlatformLowPowerStats(
- [&data, wallClockTimestampNs, elapsedTimestampNs]
- (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>(
- android::util::SUBSYSTEM_SLEEP_STATE,
- wallClockTimestampNs, elapsedTimestampNs);
- statePtr->write(state.name);
- statePtr->write("");
- statePtr->write(state.totalTransitions);
- statePtr->write(state.residencyInMsecSinceBoot);
- 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 (const auto& voter : state.voters) {
- auto voterPtr = make_shared<LogEvent>(
- android::util::SUBSYSTEM_SLEEP_STATE,
- wallClockTimestampNs, elapsedTimestampNs);
- voterPtr->write(state.name);
- voterPtr->write(voter.name);
- voterPtr->write(voter.totalNumberOfTimesVotedSinceBoot);
- voterPtr->write(voter.totalTimeInMsecVotedForSinceBoot);
- voterPtr->init();
- data->push_back(voterPtr);
- VLOG("powerstatevoter: %s, %s, %lld, %lld", state.name.c_str(),
- voter.name.c_str(),
- (long long)voter.totalTimeInMsecVotedForSinceBoot,
- (long long)voter.totalNumberOfTimesVotedSinceBoot);
- }
- }
- });
- if (!checkResultLocked(ret, __func__)) {
- return false;
- }
-
- // Trying to cast to IPower 1.1, this will succeed only for devices supporting 1.1
- sp<android::hardware::power::V1_1::IPower> gPowerHal_1_1 =
- android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
- if (gPowerHal_1_1 != nullptr) {
- ret = gPowerHal_1_1->getSubsystemLowPowerStats(
- [&data, wallClockTimestampNs, elapsedTimestampNs]
- (hidl_vec<PowerStateSubsystem> subsystems, Status status) {
- if (status != Status::SUCCESS) return;
-
- if (subsystems.size() > 0) {
- for (size_t i = 0; i < subsystems.size(); i++) {
- 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>(
- android::util::SUBSYSTEM_SLEEP_STATE,
- wallClockTimestampNs, elapsedTimestampNs);
- subsystemStatePtr->write(subsystem.name);
- subsystemStatePtr->write(state.name);
- subsystemStatePtr->write(state.totalTransitions);
- subsystemStatePtr->write(state.residencyInMsecSinceBoot);
- subsystemStatePtr->init();
- data->push_back(subsystemStatePtr);
- VLOG("subsystemstate: %s, %s, %lld, %lld, %lld",
- subsystem.name.c_str(), state.name.c_str(),
- (long long)state.residencyInMsecSinceBoot,
- (long long)state.totalTransitions,
- (long long)state.lastEntryTimestampMs);
- }
- }
- }
- });
- }
- return true;
-}
-
-// The caller must be holding gPowerHalMutex.
-std::function<bool(vector<shared_ptr<LogEvent>>* data)> getPullerLocked() {
- std::function<bool(vector<shared_ptr<LogEvent>>* data)> ret = {};
-
- // First see if power.stats HAL is available. Fall back to power HAL if
- // power.stats HAL is unavailable.
- if(android::hardware::power::stats::V1_0::IPowerStats::getService() != nullptr) {
- ALOGI("Using power.stats HAL");
- ret = getIPowerStatsDataLocked;
- } else if(android::hardware::power::V1_0::IPower::getService() != nullptr) {
- ALOGI("Using power HAL");
- ret = getIPowerDataLocked;
- }
-
- return ret;
-}
-
-bool SubsystemSleepStatePuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
- std::lock_guard<std::mutex> lock(gPowerHalMutex);
-
- if(!gPuller) {
- gPuller = getPullerLocked();
- }
-
- if(gPuller) {
- return gPuller(data);
- }
-
- ALOGE("Unable to load Power Hal or power.stats HAL");
- return false;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/TrainInfoPuller.cpp b/cmds/statsd/src/external/TrainInfoPuller.cpp
index 9d0924297912..3837f4a1a517 100644
--- a/cmds/statsd/src/external/TrainInfoPuller.cpp
+++ b/cmds/statsd/src/external/TrainInfoPuller.cpp
@@ -22,7 +22,7 @@
#include "TrainInfoPuller.h"
#include "logd/LogEvent.h"
#include "stats_log_util.h"
-#include "statslog.h"
+#include "statslog_statsd.h"
#include "storage/StorageManager.h"
using std::make_shared;
@@ -33,18 +33,20 @@ namespace os {
namespace statsd {
TrainInfoPuller::TrainInfoPuller() :
- StatsPuller(android::util::TRAIN_INFO) {
+ StatsPuller(util::TRAIN_INFO) {
}
bool TrainInfoPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
- InstallTrainInfo trainInfo;
- bool ret = StorageManager::readTrainInfo(trainInfo);
- if (!ret) {
- ALOGW("Failed to read train info.");
- return false;
+ vector<InstallTrainInfo> trainInfoList =
+ StorageManager::readAllTrainInfo();
+ if (trainInfoList.empty()) {
+ ALOGW("Train info was empty.");
+ return true;
+ }
+ for (InstallTrainInfo& trainInfo : trainInfoList) {
+ auto event = make_shared<LogEvent>(getWallClockNs(), getElapsedRealtimeNs(), trainInfo);
+ data->push_back(event);
}
- auto event = make_shared<LogEvent>(getWallClockNs(), getElapsedRealtimeNs(), trainInfo);
- data->push_back(event);
return true;
}
diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp
index ccfd666573c4..aa99d0082bdd 100644
--- a/cmds/statsd/src/external/puller_util.cpp
+++ b/cmds/statsd/src/external/puller_util.cpp
@@ -17,20 +17,13 @@
#define DEBUG false // STOPSHIP if true
#include "Log.h"
-#include "StatsPullerManager.h"
-#include "atoms_info.h"
#include "puller_util.h"
namespace android {
namespace os {
namespace statsd {
-using std::list;
-using std::map;
-using std::set;
-using std::shared_ptr;
-using std::sort;
-using std::vector;
+using namespace std;
/**
* Process all data and merge isolated with host if necessary.
@@ -54,16 +47,14 @@ using std::vector;
* All atoms should be of the same tagId. All fields should be present.
*/
void mapAndMergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap,
- int tagId) {
- if (StatsPullerManager::kAllPullAtomInfo.find(tagId) ==
- StatsPullerManager::kAllPullAtomInfo.end()) {
- VLOG("Unknown pull atom id %d", tagId);
- return;
- }
- if ((android::util::AtomsInfo::kAtomsWithAttributionChain.find(tagId) ==
- android::util::AtomsInfo::kAtomsWithAttributionChain.end()) &&
- (android::util::AtomsInfo::kAtomsWithUidField.find(tagId) ==
- android::util::AtomsInfo::kAtomsWithUidField.end())) {
+ int tagId, const vector<int>& additiveFieldsVec) {
+ // Check the first LogEvent for attribution chain or a uid field as either all atoms with this
+ // tagId have them or none of them do.
+ std::pair<int, int> attrIndexRange;
+ const bool hasAttributionChain = data[0]->hasAttributionChain(&attrIndexRange);
+ bool hasUidField = (data[0]->getUidFieldIndex() != -1);
+
+ if (!hasAttributionChain && !hasUidField) {
VLOG("No uid or attribution chain to merge, atom %d", tagId);
return;
}
@@ -74,31 +65,23 @@ void mapAndMergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const
ALOGE("Wrong atom. Expecting %d, got %d", tagId, event->GetTagId());
return;
}
- if (android::util::AtomsInfo::kAtomsWithAttributionChain.find(tagId) !=
- android::util::AtomsInfo::kAtomsWithAttributionChain.end()) {
- for (auto& value : *(event->getMutableValues())) {
- if (value.mField.getPosAtDepth(0) > kAttributionField) {
- break;
- }
- if (isAttributionUidField(value)) {
- const int hostUid = uidMap->getHostUidOrSelf(value.mValue.int_value);
- value.mValue.setInt(hostUid);
+ if (hasAttributionChain) {
+ vector<FieldValue>* const fieldValues = event->getMutableValues();
+ for (int i = attrIndexRange.first; i <= attrIndexRange.second; i++) {
+ FieldValue& fieldValue = fieldValues->at(i);
+ if (isAttributionUidField(fieldValue)) {
+ const int hostUid = uidMap->getHostUidOrSelf(fieldValue.mValue.int_value);
+ fieldValue.mValue.setInt(hostUid);
}
}
} else {
- auto it = android::util::AtomsInfo::kAtomsWithUidField.find(tagId);
- if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
- int uidField = it->second; // uidField is the field number in proto,
- // starting from 1
- if (uidField > 0 && (int)event->getValues().size() >= uidField &&
- (event->getValues())[uidField - 1].mValue.getType() == INT) {
- Value& value = (*event->getMutableValues())[uidField - 1].mValue;
- const int hostUid = uidMap->getHostUidOrSelf(value.int_value);
- value.setInt(hostUid);
- } else {
- ALOGE("Malformed log, uid not found. %s", event->ToString().c_str());
- return;
- }
+ int uidFieldIndex = event->getUidFieldIndex();
+ if (uidFieldIndex != -1) {
+ Value& value = (*event->getMutableValues())[uidFieldIndex].mValue;
+ const int hostUid = uidMap->getHostUidOrSelf(value.int_value);
+ value.setInt(hostUid);
+ } else {
+ ALOGE("Malformed log, uid not found. %s", event->ToString().c_str());
}
}
}
@@ -120,8 +103,6 @@ void mapAndMergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const
});
vector<shared_ptr<LogEvent>> mergedData;
- const vector<int>& additiveFieldsVec =
- StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.additiveFields;
const set<int> additiveFields(additiveFieldsVec.begin(), additiveFieldsVec.end());
bool needMerge = true;
diff --git a/cmds/statsd/src/external/puller_util.h b/cmds/statsd/src/external/puller_util.h
index f703e6c589c1..afcf68ca10ad 100644
--- a/cmds/statsd/src/external/puller_util.h
+++ b/cmds/statsd/src/external/puller_util.h
@@ -26,7 +26,8 @@ namespace os {
namespace statsd {
void mapAndMergeIsolatedUidsToHostUid(std::vector<std::shared_ptr<LogEvent>>& data,
- const sp<UidMap>& uidMap, int tagId);
+ const sp<UidMap>& uidMap, int tagId,
+ const vector<int>& additiveFieldsVec);
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 3054b6d2204b..6e89038f4152 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -20,7 +20,7 @@
#include <android/util/ProtoOutputStream.h>
#include "../stats_log_util.h"
-#include "statslog.h"
+#include "statslog_statsd.h"
#include "storage/StorageManager.h"
namespace android {
@@ -38,6 +38,7 @@ using android::util::ProtoOutputStream;
using std::lock_guard;
using std::shared_ptr;
using std::string;
+using std::to_string;
using std::vector;
const int FIELD_ID_BEGIN_TIME = 1;
@@ -54,6 +55,7 @@ const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL = 19;
const int FIELD_ID_ATOM_STATS_TAG = 1;
const int FIELD_ID_ATOM_STATS_COUNT = 2;
+const int FIELD_ID_ATOM_STATS_ERROR_COUNT = 3;
const int FIELD_ID_ANOMALY_ALARMS_REGISTERED = 1;
const int FIELD_ID_PERIODIC_ALARMS_REGISTERED = 1;
@@ -113,13 +115,13 @@ const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_UID = 1;
const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_TIME = 2;
const std::map<int, std::pair<size_t, size_t>> StatsdStats::kAtomDimensionKeySizeLimitMap = {
- {android::util::BINDER_CALLS, {6000, 10000}},
- {android::util::LOOPER_STATS, {1500, 2500}},
- {android::util::CPU_TIME_PER_UID_FREQ, {6000, 10000}},
+ {util::BINDER_CALLS, {6000, 10000}},
+ {util::LOOPER_STATS, {1500, 2500}},
+ {util::CPU_TIME_PER_UID_FREQ, {6000, 10000}},
};
StatsdStats::StatsdStats() {
- mPushedAtomStats.resize(android::util::kMaxPushedAtomId + 1);
+ mPushedAtomStats.resize(kMaxPushedAtomId + 1);
mStartTimeSec = getWallClockSec();
}
@@ -435,9 +437,18 @@ void StatsdStats::notePullDataError(int pullAtomId) {
mPulledAtomStats[pullAtomId].dataError++;
}
-void StatsdStats::notePullTimeout(int pullAtomId) {
+void StatsdStats::notePullTimeout(int pullAtomId,
+ int64_t pullUptimeMillis,
+ int64_t pullElapsedMillis) {
lock_guard<std::mutex> lock(mLock);
- mPulledAtomStats[pullAtomId].pullTimeout++;
+ PulledAtomStats& pulledAtomStats = mPulledAtomStats[pullAtomId];
+ pulledAtomStats.pullTimeout++;
+
+ if (pulledAtomStats.pullTimeoutMetadata.size() == kMaxTimestampCount) {
+ pulledAtomStats.pullTimeoutMetadata.pop_front();
+ }
+
+ pulledAtomStats.pullTimeoutMetadata.emplace_back(pullUptimeMillis, pullElapsedMillis);
}
void StatsdStats::notePullExceedMaxDelay(int pullAtomId) {
@@ -448,7 +459,7 @@ void StatsdStats::notePullExceedMaxDelay(int pullAtomId) {
void StatsdStats::noteAtomLogged(int atomId, int32_t timeSec) {
lock_guard<std::mutex> lock(mLock);
- if (atomId <= android::util::kMaxPushedAtomId) {
+ if (atomId <= kMaxPushedAtomId) {
mPushedAtomStats[atomId]++;
} else {
if (mNonPlatformPushedAtomStats.size() < kMaxNonPlatformPushedAtoms) {
@@ -471,14 +482,19 @@ void StatsdStats::notePullFailed(int atomId) {
mPulledAtomStats[atomId].pullFailed++;
}
-void StatsdStats::noteStatsCompanionPullFailed(int atomId) {
+void StatsdStats::notePullUidProviderNotFound(int atomId) {
+ lock_guard<std::mutex> lock(mLock);
+ mPulledAtomStats[atomId].pullUidProviderNotFound++;
+}
+
+void StatsdStats::notePullerNotFound(int atomId) {
lock_guard<std::mutex> lock(mLock);
- mPulledAtomStats[atomId].statsCompanionPullFailed++;
+ mPulledAtomStats[atomId].pullerNotFound++;
}
-void StatsdStats::noteStatsCompanionPullBinderTransactionFailed(int atomId) {
+void StatsdStats::notePullBinderCallFailed(int atomId) {
lock_guard<std::mutex> lock(mLock);
- mPulledAtomStats[atomId].statsCompanionPullBinderTransactionFailed++;
+ mPulledAtomStats[atomId].binderCallFailCount++;
}
void StatsdStats::noteEmptyData(int atomId) {
@@ -549,6 +565,20 @@ void StatsdStats::noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayN
std::min(pullStats.minBucketBoundaryDelayNs, timeDelayNs);
}
+void StatsdStats::noteAtomError(int atomTag, bool pull) {
+ lock_guard<std::mutex> lock(mLock);
+ if (pull) {
+ mPulledAtomStats[atomTag].atomErrorCount++;
+ return;
+ }
+
+ bool present = (mPushedAtomErrorStats.find(atomTag) != mPushedAtomErrorStats.end());
+ bool full = (mPushedAtomErrorStats.size() >= (size_t)kMaxPushedAtomErrorStatsSize);
+ if (!full || present) {
+ mPushedAtomErrorStats[atomTag]++;
+ }
+}
+
StatsdStats::AtomMetricStats& StatsdStats::getAtomMetricStats(int64_t metricId) {
auto atomMetricStatsIter = mAtomMetricStats.find(metricId);
if (atomMetricStatsIter != mAtomMetricStats.end()) {
@@ -593,6 +623,7 @@ void StatsdStats::resetInternalLocked() {
for (auto& pullStats : mPulledAtomStats) {
pullStats.second.totalPull = 0;
pullStats.second.totalPullFromCache = 0;
+ pullStats.second.minPullIntervalSec = LONG_MAX;
pullStats.second.avgPullTimeNs = 0;
pullStats.second.maxPullTimeNs = 0;
pullStats.second.numPullTime = 0;
@@ -602,11 +633,18 @@ void StatsdStats::resetInternalLocked() {
pullStats.second.dataError = 0;
pullStats.second.pullTimeout = 0;
pullStats.second.pullExceedMaxDelay = 0;
+ pullStats.second.pullFailed = 0;
+ pullStats.second.pullUidProviderNotFound = 0;
+ pullStats.second.pullerNotFound = 0;
pullStats.second.registeredCount = 0;
pullStats.second.unregisteredCount = 0;
+ pullStats.second.atomErrorCount = 0;
+ pullStats.second.binderCallFailCount = 0;
+ pullStats.second.pullTimeoutMetadata.clear();
}
mAtomMetricStats.clear();
mActivationBroadcastGuardrailStats.clear();
+ mPushedAtomErrorStats.clear();
}
string buildTimeString(int64_t timeSec) {
@@ -617,6 +655,15 @@ string buildTimeString(int64_t timeSec) {
return string(timeBuffer);
}
+int StatsdStats::getPushedAtomErrors(int atomId) const {
+ const auto& it = mPushedAtomErrorStats.find(atomId);
+ if (it != mPushedAtomErrorStats.end()) {
+ return it->second;
+ } else {
+ return 0;
+ }
+}
+
void StatsdStats::dumpStats(int out) const {
lock_guard<std::mutex> lock(mLock);
time_t t = mStartTimeSec;
@@ -721,11 +768,13 @@ void StatsdStats::dumpStats(int out) const {
const size_t atomCounts = mPushedAtomStats.size();
for (size_t i = 2; i < atomCounts; i++) {
if (mPushedAtomStats[i] > 0) {
- dprintf(out, "Atom %lu->%d\n", (unsigned long)i, mPushedAtomStats[i]);
+ dprintf(out, "Atom %zu->(total count)%d, (error count)%d\n", i, mPushedAtomStats[i],
+ getPushedAtomErrors((int)i));
}
}
for (const auto& pair : mNonPlatformPushedAtomStats) {
- dprintf(out, "Atom %lu->%d\n", (unsigned long)pair.first, pair.second);
+ dprintf(out, "Atom %d->(total count)%d, (error count)%d\n", pair.first, pair.second,
+ getPushedAtomErrors(pair.first));
}
dprintf(out, "********Pulled Atom stats***********\n");
@@ -736,14 +785,32 @@ void StatsdStats::dumpStats(int out) const {
" (average pull time nanos)%lld, (max pull time nanos)%lld, (average pull delay "
"nanos)%lld, "
" (max pull delay nanos)%lld, (data error)%ld\n"
- " (pull timeout)%ld, (pull exceed max delay)%ld\n"
- " (registered count) %ld, (unregistered count) %ld\n",
+ " (pull timeout)%ld, (pull exceed max delay)%ld"
+ " (no uid provider count)%ld, (no puller found count)%ld\n"
+ " (registered count) %ld, (unregistered count) %ld"
+ " (atom error count) %d\n",
(int)pair.first, (long)pair.second.totalPull, (long)pair.second.totalPullFromCache,
(long)pair.second.pullFailed, (long)pair.second.minPullIntervalSec,
(long long)pair.second.avgPullTimeNs, (long long)pair.second.maxPullTimeNs,
(long long)pair.second.avgPullDelayNs, (long long)pair.second.maxPullDelayNs,
pair.second.dataError, pair.second.pullTimeout, pair.second.pullExceedMaxDelay,
- pair.second.registeredCount, pair.second.unregisteredCount);
+ pair.second.pullUidProviderNotFound, pair.second.pullerNotFound,
+ pair.second.registeredCount, pair.second.unregisteredCount,
+ pair.second.atomErrorCount);
+ if (pair.second.pullTimeoutMetadata.size() > 0) {
+ string uptimeMillis = "(pull timeout system uptime millis) ";
+ string pullTimeoutMillis = "(pull timeout elapsed time millis) ";
+ for (const auto& stats : pair.second.pullTimeoutMetadata) {
+ uptimeMillis.append(to_string(stats.pullTimeoutUptimeMillis)).append(",");;
+ pullTimeoutMillis.append(to_string(stats.pullTimeoutElapsedMillis)).append(",");
+ }
+ uptimeMillis.pop_back();
+ uptimeMillis.push_back('\n');
+ pullTimeoutMillis.pop_back();
+ pullTimeoutMillis.push_back('\n');
+ dprintf(out, "%s", uptimeMillis.c_str());
+ dprintf(out, "%s", pullTimeoutMillis.c_str());
+ }
}
if (mAnomalyAlarmRegisteredStats > 0) {
@@ -919,6 +986,10 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) {
proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_STATS | FIELD_COUNT_REPEATED);
proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_TAG, (int32_t)i);
proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_COUNT, mPushedAtomStats[i]);
+ int errors = getPushedAtomErrors(i);
+ if (errors > 0) {
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_ERROR_COUNT, errors);
+ }
proto.end(token);
}
}
@@ -928,6 +999,10 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) {
proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_STATS | FIELD_COUNT_REPEATED);
proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_TAG, pair.first);
proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_COUNT, pair.second);
+ int errors = getPushedAtomErrors(pair.first);
+ if (errors > 0) {
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_ERROR_COUNT, errors);
+ }
proto.end(token);
}
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 564b9ee8051c..005048446fc3 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -16,7 +16,6 @@
#pragma once
#include "config/ConfigKey.h"
-#include "atoms_info.h"
#include <gtest/gtest_prod.h>
#include <log/log_time.h>
@@ -102,7 +101,7 @@ public:
// Per atom dimension key size limit
static const std::map<int, std::pair<size_t, size_t>> kAtomDimensionKeySizeLimitMap;
- const static int kMaxConfigCountPerUid = 10;
+ const static int kMaxConfigCountPerUid = 20;
const static int kMaxAlertCountPerConfig = 100;
const static int kMaxConditionCountPerConfig = 300;
const static int kMaxMetricCountPerConfig = 1000;
@@ -119,6 +118,8 @@ public:
const static int kMaxLogSourceCount = 50;
+ const static int kMaxPullAtomPackages = 100;
+
// Max memory allowed for storing metrics per configuration. If this limit is exceeded, statsd
// drops the metrics data in memory.
static const size_t kMaxMetricsBytesPerConfig = 2 * 1024 * 1024;
@@ -159,13 +160,20 @@ public:
static const long kPullerCacheClearIntervalSec = 1;
// Max time to do a pull.
- static const int64_t kPullMaxDelayNs = 10 * NS_PER_SEC;
+ static const int64_t kPullMaxDelayNs = 30 * NS_PER_SEC;
// Maximum number of pushed atoms statsd stats will track above kMaxPushedAtomId.
static const int kMaxNonPlatformPushedAtoms = 100;
- // Max platform atom tag number.
- static const int32_t kMaxPlatformAtomTag = 100000;
+ // Maximum atom id value that we consider a platform pushed atom.
+ // This should be updated once highest pushed atom id in atoms.proto approaches this value.
+ static const int kMaxPushedAtomId = 500;
+
+ // Atom id that is the start of the pulled atoms.
+ static const int kPullAtomStartTag = 10000;
+
+ // Atom id that is the start of vendor atoms.
+ static const int kVendorAtomStartTag = 100000;
// Vendor pulled atom start id.
static const int32_t kVendorPulledAtomStartTag = 150000;
@@ -181,6 +189,8 @@ public:
static const int64_t kInt64Max = 0x7fffffffffffffffLL;
+ static const int32_t kMaxLoggedBucketDropEvents = 10;
+
/**
* Report a new config has been received and report the static stats about the config.
*
@@ -342,7 +352,7 @@ public:
/*
* Records pull exceeds timeout for the puller.
*/
- void notePullTimeout(int pullAtomId);
+ void notePullTimeout(int pullAtomId, int64_t pullUptimeMillis, int64_t pullElapsedMillis);
/*
* Records pull exceeds max delay for a metric.
@@ -361,21 +371,30 @@ public:
int32_t lastAtomTag, int32_t uid, int32_t pid);
/**
- * Records that the pull of an atom has failed
+ * Records that the pull of an atom has failed. Eg, if the client indicated the pull failed, if
+ * the pull timed out, or if the outgoing binder call failed.
+ * This count will only increment if the puller was actually invoked.
+ *
+ * It does not include a pull not occurring due to not finding the appropriate
+ * puller. These cases are covered in other counts.
*/
void notePullFailed(int atomId);
/**
- * Records that the pull of StatsCompanionService atom has failed
+ * Records that the pull of an atom has failed due to not having a uid provider.
*/
- void noteStatsCompanionPullFailed(int atomId);
+ void notePullUidProviderNotFound(int atomId);
/**
- * Records that the pull of a StatsCompanionService atom has failed due to a failed binder
- * transaction. This can happen when StatsCompanionService returns too
- * much data (the max Binder parcel size is 1MB)
+ * Records that the pull of an atom has failed due not finding a puller registered by a
+ * trusted uid.
*/
- void noteStatsCompanionPullBinderTransactionFailed(int atomId);
+ void notePullerNotFound(int atomId);
+
+ /**
+ * Records that the pull has failed due to the outgoing binder call failing.
+ */
+ void notePullBinderCallFailed(int atomId);
/**
* A pull with no data occurred
@@ -450,6 +469,16 @@ public:
*/
void noteActivationBroadcastGuardrailHit(const int uid);
+ /**
+ * Reports that an atom is erroneous or cannot be parsed successfully by
+ * statsd. An atom tag of 0 indicates that the client did not supply the
+ * atom id within the encoding.
+ *
+ * For pushed atoms only, this call should be preceded by a call to
+ * noteAtomLogged.
+ */
+ void noteAtomError(int atomTag, bool pull=false);
+
/**
* Reset the historical stats. Including all stats in icebox, and the tracked stats about
* metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue
@@ -469,6 +498,14 @@ public:
*/
void dumpStats(int outFd) const;
+ typedef struct PullTimeoutMetadata {
+ int64_t pullTimeoutUptimeMillis;
+ int64_t pullTimeoutElapsedMillis;
+ PullTimeoutMetadata(int64_t uptimeMillis, int64_t elapsedMillis) :
+ pullTimeoutUptimeMillis(uptimeMillis),
+ pullTimeoutElapsedMillis(elapsedMillis) {/* do nothing */}
+ } PullTimeoutMetadata;
+
typedef struct {
long totalPull = 0;
long totalPullFromCache = 0;
@@ -483,11 +520,14 @@ public:
long pullTimeout = 0;
long pullExceedMaxDelay = 0;
long pullFailed = 0;
- long statsCompanionPullFailed = 0;
- long statsCompanionPullBinderTransactionFailed = 0;
+ long pullUidProviderNotFound = 0;
+ long pullerNotFound = 0;
long emptyData = 0;
long registeredCount = 0;
long unregisteredCount = 0;
+ int32_t atomErrorCount = 0;
+ long binderCallFailCount = 0;
+ std::list<PullTimeoutMetadata> pullTimeoutMetadata;
} PulledAtomStats;
typedef struct {
@@ -535,6 +575,12 @@ private:
// Maps PullAtomId to its stats. The size is capped by the puller atom counts.
std::map<int, PulledAtomStats> mPulledAtomStats;
+ // Stores the number of times a pushed atom was logged erroneously. The
+ // corresponding counts for pulled atoms are stored in PulledAtomStats.
+ // The max size of this map is kMaxAtomErrorsStatsSize.
+ std::map<int, int> mPushedAtomErrorStats;
+ int kMaxPushedAtomErrorStatsSize = 100;
+
// Maps metric ID to its stats. The size is capped by the number of metrics.
std::map<int64_t, AtomMetricStats> mAtomMetricStats;
@@ -602,6 +648,8 @@ private:
void addToIceBoxLocked(std::shared_ptr<ConfigStats>& stats);
+ int getPushedAtomErrors(int atomId) const;
+
/**
* Get a reference to AtomMetricStats for a metric. If none exists, create it. The reference
* will live as long as `this`.
@@ -620,6 +668,9 @@ private:
FRIEND_TEST(StatsdStatsTest, TestPullAtomStats);
FRIEND_TEST(StatsdStatsTest, TestAtomMetricsStats);
FRIEND_TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit);
+ FRIEND_TEST(StatsdStatsTest, TestAtomErrorStats);
+
+ FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved);
};
} // namespace statsd
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 1d51ab82efa8..f56fa6221bc9 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -17,12 +17,14 @@
#define DEBUG false // STOPSHIP if true
#include "logd/LogEvent.h"
-#include "stats_log_util.h"
-#include "statslog.h"
-
-#include <binder/IPCThreadState.h>
+#include <android-base/stringprintf.h>
+#include <android/binder_ibinder.h>
#include <private/android_filesystem_config.h>
+#include "annotations.h"
+#include "stats_log_util.h"
+#include "statslog_statsd.h"
+
namespace android {
namespace os {
namespace statsd {
@@ -31,160 +33,41 @@ namespace statsd {
const int FIELD_ID_EXPERIMENT_ID = 1;
using namespace android::util;
+using android::base::StringPrintf;
using android::util::ProtoOutputStream;
using std::string;
using std::vector;
-LogEvent::LogEvent(log_msg& msg) {
- mContext =
- create_android_log_parser(msg.msg() + sizeof(uint32_t), msg.len() - sizeof(uint32_t));
- mLogdTimestampNs = msg.entry.sec * NS_PER_SEC + msg.entry.nsec;
- mLogUid = msg.entry.uid;
- init(mContext);
- if (mContext) {
- // android_log_destroy will set mContext to NULL
- android_log_destroy(&mContext);
- }
-}
-
-LogEvent::LogEvent(const LogEvent& event) {
- mTagId = event.mTagId;
- mLogUid = event.mLogUid;
- mElapsedTimestampNs = event.mElapsedTimestampNs;
- mLogdTimestampNs = event.mLogdTimestampNs;
- mValues = event.mValues;
-}
-
-LogEvent::LogEvent(const StatsLogEventWrapper& statsLogEventWrapper, int workChainIndex) {
- mTagId = statsLogEventWrapper.getTagId();
- mLogdTimestampNs = statsLogEventWrapper.getWallClockTimeNs();
- mElapsedTimestampNs = statsLogEventWrapper.getElapsedRealTimeNs();
- mLogUid = 0;
- int workChainPosOffset = 0;
- if (workChainIndex != -1) {
- const WorkChain& wc = statsLogEventWrapper.getWorkChains()[workChainIndex];
- // chains are at field 1, level 2
- int depth = 2;
- for (int i = 0; i < (int)wc.uids.size(); i++) {
- int pos[] = {1, i + 1, 1};
- mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(wc.uids[i])));
- pos[2]++;
- mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(wc.tags[i])));
- mValues.back().mField.decorateLastPos(2);
- }
- mValues.back().mField.decorateLastPos(1);
- workChainPosOffset = 1;
- }
- for (int i = 0; i < (int)statsLogEventWrapper.getElements().size(); i++) {
- Field field(statsLogEventWrapper.getTagId(), getSimpleField(i + 1 + workChainPosOffset));
- switch (statsLogEventWrapper.getElements()[i].type) {
- case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::INT:
- mValues.push_back(
- FieldValue(field, Value(statsLogEventWrapper.getElements()[i].int_value)));
- break;
- case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::LONG:
- mValues.push_back(
- FieldValue(field, Value(statsLogEventWrapper.getElements()[i].long_value)));
- break;
- case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::FLOAT:
- mValues.push_back(FieldValue(
- field, Value(statsLogEventWrapper.getElements()[i].float_value)));
- break;
- case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::DOUBLE:
- mValues.push_back(FieldValue(
- field, Value(statsLogEventWrapper.getElements()[i].double_value)));
- break;
- case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::STRING:
- mValues.push_back(
- FieldValue(field, Value(statsLogEventWrapper.getElements()[i].str_value)));
- break;
- case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::STORAGE:
- mValues.push_back(FieldValue(
- field, Value(statsLogEventWrapper.getElements()[i].storage_value)));
- break;
- default:
- break;
- }
- }
-}
-
-void LogEvent::createLogEvents(const StatsLogEventWrapper& statsLogEventWrapper,
- std::vector<std::shared_ptr<LogEvent>>& logEvents) {
- if (statsLogEventWrapper.getWorkChains().size() == 0) {
- logEvents.push_back(std::make_shared<LogEvent>(statsLogEventWrapper, -1));
- } else {
- for (size_t i = 0; i < statsLogEventWrapper.getWorkChains().size(); i++) {
- logEvents.push_back(std::make_shared<LogEvent>(statsLogEventWrapper, i));
- }
- }
-}
-
-LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs) {
- mLogdTimestampNs = wallClockTimestampNs;
- mElapsedTimestampNs = elapsedTimestampNs;
- mTagId = tagId;
- mLogUid = 0;
- mContext = create_android_logger(1937006964); // the event tag shared by all stats logs
- if (mContext) {
- android_log_write_int64(mContext, elapsedTimestampNs);
- android_log_write_int32(mContext, tagId);
- }
-}
-
-LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
- int32_t uid,
- const std::map<int32_t, int32_t>& int_map,
- const std::map<int32_t, int64_t>& long_map,
- const std::map<int32_t, std::string>& string_map,
- const std::map<int32_t, float>& float_map) {
- mLogdTimestampNs = wallClockTimestampNs;
- mElapsedTimestampNs = elapsedTimestampNs;
- mTagId = android::util::KEY_VALUE_PAIRS_ATOM;
- mLogUid = uid;
-
- int pos[] = {1, 1, 1};
-
- mValues.push_back(FieldValue(Field(mTagId, pos, 0 /* depth */), Value(uid)));
- pos[0]++;
- for (const auto&itr : int_map) {
- pos[2] = 1;
- mValues.push_back(FieldValue(Field(mTagId, pos, 2 /* depth */), Value(itr.first)));
- pos[2] = 2;
- mValues.push_back(FieldValue(Field(mTagId, pos, 2 /* depth */), Value(itr.second)));
- mValues.back().mField.decorateLastPos(2);
- pos[1]++;
- }
-
- for (const auto&itr : long_map) {
- pos[2] = 1;
- mValues.push_back(FieldValue(Field(mTagId, pos, 2 /* depth */), Value(itr.first)));
- pos[2] = 3;
- mValues.push_back(FieldValue(Field(mTagId, pos, 2 /* depth */), Value(itr.second)));
- mValues.back().mField.decorateLastPos(2);
- pos[1]++;
- }
-
- for (const auto&itr : string_map) {
- pos[2] = 1;
- mValues.push_back(FieldValue(Field(mTagId, pos, 2 /* depth */), Value(itr.first)));
- pos[2] = 4;
- mValues.push_back(FieldValue(Field(mTagId, pos, 2 /* depth */), Value(itr.second)));
- mValues.back().mField.decorateLastPos(2);
- pos[1]++;
- }
-
- for (const auto&itr : float_map) {
- pos[2] = 1;
- mValues.push_back(FieldValue(Field(mTagId, pos, 2 /* depth */), Value(itr.first)));
- pos[2] = 5;
- mValues.push_back(FieldValue(Field(mTagId, pos, 2 /* depth */), Value(itr.second)));
- mValues.back().mField.decorateLastPos(2);
- pos[1]++;
- }
- if (!mValues.empty()) {
- mValues.back().mField.decorateLastPos(1);
- mValues.at(mValues.size() - 2).mField.decorateLastPos(1);
- }
+// stats_event.h socket types. Keep in sync.
+/* ERRORS */
+#define ERROR_NO_TIMESTAMP 0x1
+#define ERROR_NO_ATOM_ID 0x2
+#define ERROR_OVERFLOW 0x4
+#define ERROR_ATTRIBUTION_CHAIN_TOO_LONG 0x8
+#define ERROR_TOO_MANY_KEY_VALUE_PAIRS 0x10
+#define ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD 0x20
+#define ERROR_INVALID_ANNOTATION_ID 0x40
+#define ERROR_ANNOTATION_ID_TOO_LARGE 0x80
+#define ERROR_TOO_MANY_ANNOTATIONS 0x100
+#define ERROR_TOO_MANY_FIELDS 0x200
+#define ERROR_INVALID_VALUE_TYPE 0x400
+#define ERROR_STRING_NOT_NULL_TERMINATED 0x800
+
+/* TYPE IDS */
+#define INT32_TYPE 0x00
+#define INT64_TYPE 0x01
+#define STRING_TYPE 0x02
+#define LIST_TYPE 0x03
+#define FLOAT_TYPE 0x04
+#define BOOL_TYPE 0x05
+#define BYTE_ARRAY_TYPE 0x06
+#define OBJECT_TYPE 0x07
+#define KEY_VALUE_PAIRS_TYPE 0x08
+#define ATTRIBUTION_CHAIN_TYPE 0x09
+#define ERROR_TYPE 0x0F
+
+LogEvent::LogEvent(int32_t uid, int32_t pid)
+ : mLogdTimestampNs(time(nullptr)), mLogUid(uid), mLogPid(pid) {
}
LogEvent::LogEvent(const string& trainName, int64_t trainVersionCode, bool requiresStaging,
@@ -192,8 +75,9 @@ LogEvent::LogEvent(const string& trainName, int64_t trainVersionCode, bool requi
const std::vector<uint8_t>& experimentIds, int32_t userId) {
mLogdTimestampNs = getWallClockNs();
mElapsedTimestampNs = getElapsedRealtimeNs();
- mTagId = android::util::BINARY_PUSH_STATE_CHANGED;
- mLogUid = android::IPCThreadState::self()->getCallingUid();
+ mTagId = util::BINARY_PUSH_STATE_CHANGED;
+ mLogUid = AIBinder_getCallingUid();
+ mLogPid = AIBinder_getCallingPid();
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)), Value(trainName)));
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainVersionCode)));
@@ -210,7 +94,7 @@ LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
const InstallTrainInfo& trainInfo) {
mLogdTimestampNs = wallClockTimestampNs;
mElapsedTimestampNs = elapsedTimestampNs;
- mTagId = android::util::TRAIN_INFO;
+ mTagId = util::TRAIN_INFO;
mValues.push_back(
FieldValue(Field(mTagId, getSimpleField(1)), Value(trainInfo.trainVersionCode)));
@@ -221,309 +105,320 @@ LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value(trainInfo.status)));
}
-LogEvent::LogEvent(int32_t tagId, int64_t timestampNs) : LogEvent(tagId, timestampNs, timestampNs) {
+void LogEvent::parseInt32(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
+ int32_t value = readNextValue<int32_t>();
+ addToValues(pos, depth, value, last);
+ parseAnnotations(numAnnotations);
}
-LogEvent::LogEvent(int32_t tagId, int64_t timestampNs, int32_t uid) {
- mLogdTimestampNs = timestampNs;
- mTagId = tagId;
- mLogUid = uid;
- mContext = create_android_logger(1937006964); // the event tag shared by all stats logs
- if (mContext) {
- android_log_write_int64(mContext, timestampNs);
- android_log_write_int32(mContext, tagId);
- }
+void LogEvent::parseInt64(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
+ int64_t value = readNextValue<int64_t>();
+ addToValues(pos, depth, value, last);
+ parseAnnotations(numAnnotations);
}
-void LogEvent::init() {
- if (mContext) {
- const char* buffer;
- size_t len = android_log_write_list_buffer(mContext, &buffer);
- // turns to reader mode
- android_log_context contextForRead = create_android_log_parser(buffer, len);
- if (contextForRead) {
- init(contextForRead);
- // destroy the context to save memory.
- // android_log_destroy will set mContext to NULL
- android_log_destroy(&contextForRead);
- }
- android_log_destroy(&mContext);
+void LogEvent::parseString(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
+ int32_t numBytes = readNextValue<int32_t>();
+ if ((uint32_t)numBytes > mRemainingLen) {
+ mValid = false;
+ return;
}
+
+ string value = string((char*)mBuf, numBytes);
+ mBuf += numBytes;
+ mRemainingLen -= numBytes;
+ addToValues(pos, depth, value, last);
+ parseAnnotations(numAnnotations);
}
-LogEvent::~LogEvent() {
- if (mContext) {
- // This is for the case when LogEvent is created using the test interface
- // but init() isn't called.
- android_log_destroy(&mContext);
- }
+void LogEvent::parseFloat(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
+ float value = readNextValue<float>();
+ addToValues(pos, depth, value, last);
+ parseAnnotations(numAnnotations);
}
-bool LogEvent::write(int32_t value) {
- if (mContext) {
- return android_log_write_int32(mContext, value) >= 0;
- }
- return false;
+void LogEvent::parseBool(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
+ // cast to int32_t because FieldValue does not support bools
+ int32_t value = (int32_t)readNextValue<uint8_t>();
+ addToValues(pos, depth, value, last);
+ parseAnnotations(numAnnotations);
}
-bool LogEvent::write(uint32_t value) {
- if (mContext) {
- return android_log_write_int32(mContext, value) >= 0;
+void LogEvent::parseByteArray(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
+ int32_t numBytes = readNextValue<int32_t>();
+ if ((uint32_t)numBytes > mRemainingLen) {
+ mValid = false;
+ return;
}
- return false;
+
+ vector<uint8_t> value(mBuf, mBuf + numBytes);
+ mBuf += numBytes;
+ mRemainingLen -= numBytes;
+ addToValues(pos, depth, value, last);
+ parseAnnotations(numAnnotations);
}
-bool LogEvent::write(int64_t value) {
- if (mContext) {
- return android_log_write_int64(mContext, value) >= 0;
+void LogEvent::parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
+ int32_t numPairs = readNextValue<uint8_t>();
+
+ for (pos[1] = 1; pos[1] <= numPairs; pos[1]++) {
+ last[1] = (pos[1] == numPairs);
+
+ // parse key
+ pos[2] = 1;
+ parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0);
+
+ // parse value
+ last[2] = true;
+
+ uint8_t typeInfo = readNextValue<uint8_t>();
+ switch (getTypeId(typeInfo)) {
+ case INT32_TYPE:
+ pos[2] = 2; // pos[2] determined by index of type in KeyValuePair in atoms.proto
+ parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0);
+ break;
+ case INT64_TYPE:
+ pos[2] = 3;
+ parseInt64(pos, /*depth=*/2, last, /*numAnnotations=*/0);
+ break;
+ case STRING_TYPE:
+ pos[2] = 4;
+ parseString(pos, /*depth=*/2, last, /*numAnnotations=*/0);
+ break;
+ case FLOAT_TYPE:
+ pos[2] = 5;
+ parseFloat(pos, /*depth=*/2, last, /*numAnnotations=*/0);
+ break;
+ default:
+ mValid = false;
+ }
}
- return false;
+
+ parseAnnotations(numAnnotations);
+
+ pos[1] = pos[2] = 1;
+ last[1] = last[2] = false;
}
-bool LogEvent::write(uint64_t value) {
- if (mContext) {
- return android_log_write_int64(mContext, value) >= 0;
+void LogEvent::parseAttributionChain(int32_t* pos, int32_t depth, bool* last,
+ uint8_t numAnnotations) {
+ const unsigned int firstUidInChainIndex = mValues.size();
+ const int32_t numNodes = readNextValue<uint8_t>();
+ for (pos[1] = 1; pos[1] <= numNodes; pos[1]++) {
+ last[1] = (pos[1] == numNodes);
+
+ // parse uid
+ pos[2] = 1;
+ parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0);
+
+ // parse tag
+ pos[2] = 2;
+ last[2] = true;
+ parseString(pos, /*depth=*/2, last, /*numAnnotations=*/0);
}
- return false;
+ // Check if at least one node was successfully parsed.
+ if (mValues.size() - 1 > firstUidInChainIndex) {
+ mAttributionChainStartIndex = static_cast<int8_t>(firstUidInChainIndex);
+ mAttributionChainEndIndex = static_cast<int8_t>(mValues.size() - 1);
+ }
+
+ parseAnnotations(numAnnotations, firstUidInChainIndex);
+
+ pos[1] = pos[2] = 1;
+ last[1] = last[2] = false;
+}
+
+// Assumes that mValues is not empty
+bool LogEvent::checkPreviousValueType(Type expected) {
+ return mValues[mValues.size() - 1].mValue.getType() == expected;
}
-bool LogEvent::write(const string& value) {
- if (mContext) {
- return android_log_write_string8_len(mContext, value.c_str(), value.length()) >= 0;
+void LogEvent::parseIsUidAnnotation(uint8_t annotationType) {
+ if (mValues.empty() || !checkPreviousValueType(INT) || annotationType != BOOL_TYPE) {
+ mValid = false;
+ return;
}
- return false;
+
+ bool isUid = readNextValue<uint8_t>();
+ if (isUid) mUidFieldIndex = static_cast<int8_t>(mValues.size() - 1);
+ mValues[mValues.size() - 1].mAnnotations.setUidField(isUid);
}
-bool LogEvent::write(float value) {
- if (mContext) {
- return android_log_write_float32(mContext, value) >= 0;
+void LogEvent::parseTruncateTimestampAnnotation(uint8_t annotationType) {
+ if (!mValues.empty() || annotationType != BOOL_TYPE) {
+ mValid = false;
+ return;
}
- return false;
+
+ mTruncateTimestamp = readNextValue<uint8_t>();
}
-bool LogEvent::writeKeyValuePairs(int32_t uid,
- const std::map<int32_t, int32_t>& int_map,
- const std::map<int32_t, int64_t>& long_map,
- const std::map<int32_t, std::string>& string_map,
- const std::map<int32_t, float>& float_map) {
- if (mContext) {
- if (android_log_write_list_begin(mContext) < 0) {
- return false;
- }
- write(uid);
- for (const auto& itr : int_map) {
- if (android_log_write_list_begin(mContext) < 0) {
- return false;
- }
- write(itr.first);
- write(itr.second);
- if (android_log_write_list_end(mContext) < 0) {
- return false;
- }
- }
+void LogEvent::parsePrimaryFieldAnnotation(uint8_t annotationType) {
+ if (mValues.empty() || annotationType != BOOL_TYPE) {
+ mValid = false;
+ return;
+ }
- for (const auto& itr : long_map) {
- if (android_log_write_list_begin(mContext) < 0) {
- return false;
- }
- write(itr.first);
- write(itr.second);
- if (android_log_write_list_end(mContext) < 0) {
- return false;
- }
- }
+ const bool primaryField = readNextValue<uint8_t>();
+ mValues[mValues.size() - 1].mAnnotations.setPrimaryField(primaryField);
+}
- for (const auto& itr : string_map) {
- if (android_log_write_list_begin(mContext) < 0) {
- return false;
- }
- write(itr.first);
- write(itr.second.c_str());
- if (android_log_write_list_end(mContext) < 0) {
- return false;
- }
- }
+void LogEvent::parsePrimaryFieldFirstUidAnnotation(uint8_t annotationType,
+ int firstUidInChainIndex) {
+ if (mValues.empty() || annotationType != BOOL_TYPE || -1 == firstUidInChainIndex) {
+ mValid = false;
+ return;
+ }
- for (const auto& itr : float_map) {
- if (android_log_write_list_begin(mContext) < 0) {
- return false;
- }
- write(itr.first);
- write(itr.second);
- if (android_log_write_list_end(mContext) < 0) {
- return false;
- }
- }
+ const bool primaryField = readNextValue<uint8_t>();
+ mValues[firstUidInChainIndex].mAnnotations.setPrimaryField(primaryField);
+}
- if (android_log_write_list_end(mContext) < 0) {
- return false;
- }
- return true;
+void LogEvent::parseExclusiveStateAnnotation(uint8_t annotationType) {
+ if (mValues.empty() || annotationType != BOOL_TYPE) {
+ mValid = false;
+ return;
}
- return false;
+
+ const bool exclusiveState = readNextValue<uint8_t>();
+ mExclusiveStateFieldIndex = static_cast<int8_t>(mValues.size() - 1);
+ mValues[getExclusiveStateFieldIndex()].mAnnotations.setExclusiveState(exclusiveState);
}
-bool LogEvent::write(const std::vector<AttributionNodeInternal>& nodes) {
- if (mContext) {
- if (android_log_write_list_begin(mContext) < 0) {
- return false;
- }
- for (size_t i = 0; i < nodes.size(); ++i) {
- if (!write(nodes[i])) {
- return false;
- }
- }
- if (android_log_write_list_end(mContext) < 0) {
- return false;
- }
- return true;
+void LogEvent::parseTriggerStateResetAnnotation(uint8_t annotationType) {
+ if (mValues.empty() || annotationType != INT32_TYPE) {
+ mValid = false;
+ return;
}
- return false;
+
+ mResetState = readNextValue<int32_t>();
}
-bool LogEvent::write(const AttributionNodeInternal& node) {
- if (mContext) {
- if (android_log_write_list_begin(mContext) < 0) {
- return false;
- }
- if (android_log_write_int32(mContext, node.uid()) < 0) {
- return false;
- }
- if (android_log_write_string8(mContext, node.tag().c_str()) < 0) {
- return false;
- }
- if (android_log_write_list_end(mContext) < 0) {
- return false;
- }
- return true;
+void LogEvent::parseStateNestedAnnotation(uint8_t annotationType) {
+ if (mValues.empty() || annotationType != BOOL_TYPE) {
+ mValid = false;
+ return;
}
- return false;
+
+ bool nested = readNextValue<uint8_t>();
+ mValues[mValues.size() - 1].mAnnotations.setNested(nested);
}
-/**
- * The elements of each log event are stored as a vector of android_log_list_elements.
- * The goal is to do as little preprocessing as possible, because we read a tiny fraction
- * of the elements that are written to the log.
- *
- * The idea here is to read through the log items once, we get as much information we need for
- * matching as possible. Because this log will be matched against lots of matchers.
- */
-void LogEvent::init(android_log_context context) {
- android_log_list_element elem;
- int i = 0;
- int depth = -1;
- int pos[] = {1, 1, 1};
- bool isKeyValuePairAtom = false;
- do {
- elem = android_log_read_next(context);
- switch ((int)elem.type) {
- case EVENT_TYPE_INT:
- // elem at [0] is EVENT_TYPE_LIST, [1] is the timestamp, [2] is tag id.
- if (i == 2) {
- mTagId = elem.data.int32;
- isKeyValuePairAtom = (mTagId == android::util::KEY_VALUE_PAIRS_ATOM);
- } else {
- if (depth < 0 || depth > 2) {
- return;
- }
-
- mValues.push_back(
- FieldValue(Field(mTagId, pos, depth), Value((int32_t)elem.data.int32)));
-
- pos[depth]++;
- }
+// firstUidInChainIndex is a default parameter that is only needed when parsing
+// annotations for attribution chains.
+void LogEvent::parseAnnotations(uint8_t numAnnotations, int firstUidInChainIndex) {
+ for (uint8_t i = 0; i < numAnnotations; i++) {
+ uint8_t annotationId = readNextValue<uint8_t>();
+ uint8_t annotationType = readNextValue<uint8_t>();
+
+ switch (annotationId) {
+ case ANNOTATION_ID_IS_UID:
+ parseIsUidAnnotation(annotationType);
+ break;
+ case ANNOTATION_ID_TRUNCATE_TIMESTAMP:
+ parseTruncateTimestampAnnotation(annotationType);
+ break;
+ case ANNOTATION_ID_PRIMARY_FIELD:
+ parsePrimaryFieldAnnotation(annotationType);
+ break;
+ case ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID:
+ parsePrimaryFieldFirstUidAnnotation(annotationType, firstUidInChainIndex);
break;
- case EVENT_TYPE_FLOAT: {
- if (depth < 0 || depth > 2) {
- ALOGE("Depth > 2. Not supported!");
- return;
- }
-
- // Handles the oneof field in KeyValuePair atom.
- if (isKeyValuePairAtom && depth == 2) {
- pos[depth] = 5;
- }
-
- mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(elem.data.float32)));
-
- pos[depth]++;
-
- } break;
- case EVENT_TYPE_STRING: {
- if (depth < 0 || depth > 2) {
- ALOGE("Depth > 2. Not supported!");
- return;
- }
-
- // Handles the oneof field in KeyValuePair atom.
- if (isKeyValuePairAtom && depth == 2) {
- pos[depth] = 4;
- }
- mValues.push_back(FieldValue(Field(mTagId, pos, depth),
- Value(string(elem.data.string, elem.len))));
-
- pos[depth]++;
-
- } break;
- case EVENT_TYPE_LONG: {
- if (i == 1) {
- mElapsedTimestampNs = elem.data.int64;
- } else {
- if (depth < 0 || depth > 2) {
- ALOGE("Depth > 2. Not supported!");
- return;
- }
- // Handles the oneof field in KeyValuePair atom.
- if (isKeyValuePairAtom && depth == 2) {
- pos[depth] = 3;
- }
- mValues.push_back(
- FieldValue(Field(mTagId, pos, depth), Value((int64_t)elem.data.int64)));
-
- pos[depth]++;
- }
- } break;
- case EVENT_TYPE_LIST:
- depth++;
- if (depth > 2) {
- ALOGE("Depth > 2. Not supported!");
- return;
- }
- pos[depth] = 1;
+ case ANNOTATION_ID_EXCLUSIVE_STATE:
+ parseExclusiveStateAnnotation(annotationType);
+ break;
+ case ANNOTATION_ID_TRIGGER_STATE_RESET:
+ parseTriggerStateResetAnnotation(annotationType);
+ break;
+ case ANNOTATION_ID_STATE_NESTED:
+ parseStateNestedAnnotation(annotationType);
+ break;
+ default:
+ mValid = false;
+ return;
+ }
+ }
+}
+
+// This parsing logic is tied to the encoding scheme used in StatsEvent.java and
+// stats_event.c
+bool LogEvent::parseBuffer(uint8_t* buf, size_t len) {
+ mBuf = buf;
+ mRemainingLen = (uint32_t)len;
+
+ int32_t pos[] = {1, 1, 1};
+ bool last[] = {false, false, false};
+ // Beginning of buffer is OBJECT_TYPE | NUM_FIELDS | TIMESTAMP | ATOM_ID
+ uint8_t typeInfo = readNextValue<uint8_t>();
+ if (getTypeId(typeInfo) != OBJECT_TYPE) mValid = false;
+
+ uint8_t numElements = readNextValue<uint8_t>();
+ if (numElements < 2 || numElements > 127) mValid = false;
+
+ typeInfo = readNextValue<uint8_t>();
+ if (getTypeId(typeInfo) != INT64_TYPE) mValid = false;
+ mElapsedTimestampNs = readNextValue<int64_t>();
+ numElements--;
+
+ typeInfo = readNextValue<uint8_t>();
+ if (getTypeId(typeInfo) != INT32_TYPE) mValid = false;
+ mTagId = readNextValue<int32_t>();
+ numElements--;
+ parseAnnotations(getNumAnnotations(typeInfo)); // atom-level annotations
+
+ for (pos[0] = 1; pos[0] <= numElements && mValid; pos[0]++) {
+ last[0] = (pos[0] == numElements);
+
+ typeInfo = readNextValue<uint8_t>();
+ uint8_t typeId = getTypeId(typeInfo);
+
+ switch (typeId) {
+ case BOOL_TYPE:
+ parseBool(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
- case EVENT_TYPE_LIST_STOP: {
- int prevDepth = depth;
- depth--;
- if (depth >= 0 && depth < 2) {
- // Now go back to decorate the previous items that are last at prevDepth.
- // So that we can later easily match them with Position=Last matchers.
- pos[prevDepth]--;
- int path = getEncodedField(pos, prevDepth, false);
- for (auto it = mValues.rbegin(); it != mValues.rend(); ++it) {
- if (it->mField.getDepth() >= prevDepth &&
- it->mField.getPath(prevDepth) == path) {
- it->mField.decorateLastPos(prevDepth);
- } else {
- // Safe to break, because the items are in DFS order.
- break;
- }
- }
- pos[depth]++;
- }
+ case INT32_TYPE:
+ parseInt32(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
- }
- case EVENT_TYPE_UNKNOWN:
+ case INT64_TYPE:
+ parseInt64(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
+ break;
+ case FLOAT_TYPE:
+ parseFloat(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
+ break;
+ case BYTE_ARRAY_TYPE:
+ parseByteArray(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
+ break;
+ case STRING_TYPE:
+ parseString(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
+ break;
+ case KEY_VALUE_PAIRS_TYPE:
+ parseKeyValuePairs(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
+ break;
+ case ATTRIBUTION_CHAIN_TYPE:
+ parseAttributionChain(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
+ break;
+ case ERROR_TYPE:
+ /* mErrorBitmask =*/ readNextValue<int32_t>();
+ mValid = false;
break;
default:
+ mValid = false;
break;
}
- i++;
- } while ((elem.type != EVENT_TYPE_UNKNOWN) && !elem.complete);
- if (isKeyValuePairAtom && mValues.size() > 0) {
- mValues[0] = FieldValue(Field(android::util::KEY_VALUE_PAIRS_ATOM, getSimpleField(1)),
- Value((int32_t)mLogUid));
}
+
+ if (mRemainingLen != 0) mValid = false;
+ mBuf = nullptr;
+ return mValid;
+}
+
+uint8_t LogEvent::getTypeId(uint8_t typeInfo) {
+ return typeInfo & 0x0F; // type id in lower 4 bytes
+}
+
+uint8_t LogEvent::getNumAnnotations(uint8_t typeInfo) {
+ return (typeInfo >> 4) & 0x0F; // num annotations in upper 4 bytes
}
int64_t LogEvent::GetLong(size_t key, status_t* err) const {
@@ -631,6 +526,26 @@ float LogEvent::GetFloat(size_t key, status_t* err) const {
return 0.0;
}
+std::vector<uint8_t> LogEvent::GetStorage(size_t key, status_t* err) const {
+ int field = getSimpleField(key);
+ for (const auto& value : mValues) {
+ if (value.mField.getField() == field) {
+ if (value.mValue.getType() == STORAGE) {
+ return value.mValue.storage_value;
+ } else {
+ *err = BAD_TYPE;
+ return vector<uint8_t>();
+ }
+ }
+ if ((size_t)value.mField.getPosAtDepth(0) > key) {
+ break;
+ }
+ }
+
+ *err = BAD_INDEX;
+ return vector<uint8_t>();
+}
+
string LogEvent::ToString() const {
string result;
result += StringPrintf("{ uid(%d) %lld %lld (%d)", mLogUid, (long long)mLogdTimestampNs,
@@ -647,6 +562,19 @@ void LogEvent::ToProto(ProtoOutputStream& protoOutput) const {
writeFieldValueTreeToStream(mTagId, getValues(), &protoOutput);
}
+bool LogEvent::hasAttributionChain(std::pair<int, int>* indexRange) const {
+ if (mAttributionChainStartIndex == -1 || mAttributionChainEndIndex == -1) {
+ return false;
+ }
+
+ if (nullptr != indexRange) {
+ indexRange->first = static_cast<int>(mAttributionChainStartIndex);
+ indexRange->second = static_cast<int>(mAttributionChainEndIndex);
+ }
+
+ return true;
+}
+
void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds,
std::vector<uint8_t>* protoOut) {
ProtoOutputStream proto;
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index e1b5a0b8f0f5..a5f24603585a 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -18,9 +18,7 @@
#include "FieldValue.h"
-#include <android/os/StatsLogEventWrapper.h>
#include <android/util/ProtoOutputStream.h>
-#include <log/log_read.h>
#include <private/android_logger.h>
#include <string>
@@ -30,75 +28,39 @@ namespace android {
namespace os {
namespace statsd {
-struct AttributionNodeInternal {
- void set_uid(int32_t id) {
- mUid = id;
- }
-
- void set_tag(const std::string& value) {
- mTag = value;
- }
-
- int32_t uid() const {
- return mUid;
- }
-
- const std::string& tag() const {
- return mTag;
- }
-
- int32_t mUid;
- std::string mTag;
-};
-
struct InstallTrainInfo {
int64_t trainVersionCode;
std::string trainName;
int32_t status;
std::vector<int64_t> experimentIds;
+ bool requiresStaging;
+ bool rollbackEnabled;
+ bool requiresLowLatencyMonitor;
};
/**
- * Wrapper for the log_msg structure.
+ * This class decodes the structured, serialized encoding of an atom into a
+ * vector of FieldValues.
*/
class LogEvent {
public:
/**
- * Read a LogEvent from a log_msg.
- */
- explicit LogEvent(log_msg& msg);
-
- /**
- * Creates LogEvent from StatsLogEventWrapper.
- */
- static void createLogEvents(const StatsLogEventWrapper& statsLogEventWrapper,
- std::vector<std::shared_ptr<LogEvent>>& logEvents);
-
- /**
- * Construct one LogEvent from a StatsLogEventWrapper with the i-th work chain. -1 if no chain.
- */
- explicit LogEvent(const StatsLogEventWrapper& statsLogEventWrapper, int workChainIndex);
-
- /**
- * Constructs a LogEvent with synthetic data for testing. Must call init() before reading.
+ * \param uid user id of the logging caller
+ * \param pid process id of the logging caller
*/
- explicit LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs);
-
- // For testing. The timestamp is used as both elapsed real time and logd timestamp.
- explicit LogEvent(int32_t tagId, int64_t timestampNs);
-
- // For testing. The timestamp is used as both elapsed real time and logd timestamp.
- explicit LogEvent(int32_t tagId, int64_t timestampNs, int32_t uid);
+ explicit LogEvent(int32_t uid, int32_t pid);
/**
- * Constructs a KeyValuePairsAtom LogEvent from value maps.
+ * Parses the atomId, timestamp, and vector of values from a buffer
+ * containing the StatsEvent/AStatsEvent encoding of an atom.
+ *
+ * \param buf a buffer that begins at the start of the serialized atom (it
+ * should not include the android_log_header_t or the StatsEventTag)
+ * \param len size of the buffer
+ *
+ * \return success of the initialization
*/
- explicit LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
- int32_t uid,
- const std::map<int32_t, int32_t>& int_map,
- const std::map<int32_t, int64_t>& long_map,
- const std::map<int32_t, std::string>& string_map,
- const std::map<int32_t, float>& float_map);
+ bool parseBuffer(uint8_t* buf, size_t len);
// Constructs a BinaryPushStateChanged LogEvent from API call.
explicit LogEvent(const std::string& trainName, int64_t trainVersionCode, bool requiresStaging,
@@ -108,7 +70,7 @@ public:
explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
const InstallTrainInfo& installTrainInfo);
- ~LogEvent();
+ ~LogEvent() {}
/**
* Get the timestamp associated with this event.
@@ -121,9 +83,17 @@ public:
*/
inline int GetTagId() const { return mTagId; }
- inline uint32_t GetUid() const {
- return mLogUid;
- }
+ /**
+ * Get the uid of the logging client.
+ * Returns -1 if the uid is unknown/has not been set.
+ */
+ inline int32_t GetUid() const { return mLogUid; }
+
+ /**
+ * Get the pid of the logging client.
+ * Returns -1 if the pid is unknown/has not been set.
+ */
+ inline int32_t GetPid() const { return mLogPid; }
/**
* Get the nth value, starting at 1.
@@ -136,24 +106,7 @@ public:
const char* GetString(size_t key, status_t* err) const;
bool GetBool(size_t key, status_t* err) const;
float GetFloat(size_t key, status_t* err) const;
-
- /**
- * Write test data to the LogEvent. This can only be used when the LogEvent is constructed
- * using LogEvent(tagId, timestampNs). You need to call init() before you can read from it.
- */
- bool write(uint32_t value);
- bool write(int32_t value);
- bool write(uint64_t value);
- bool write(int64_t value);
- bool write(const std::string& value);
- bool write(float value);
- bool write(const std::vector<AttributionNodeInternal>& nodes);
- bool write(const AttributionNodeInternal& node);
- bool writeKeyValuePairs(int32_t uid,
- const std::map<int32_t, int32_t>& int_map,
- const std::map<int32_t, int64_t>& long_map,
- const std::map<int32_t, std::string>& string_map,
- const std::map<int32_t, float>& float_map);
+ std::vector<uint8_t> GetStorage(size_t key, status_t* err) const;
/**
* Return a string representation of this event.
@@ -166,12 +119,6 @@ public:
void ToProto(android::util::ProtoOutputStream& out) const;
/**
- * Used with the constructor where tag is passed in. Converts the log_event_list to read mode
- * and prepares the list for reading.
- */
- void init();
-
- /**
* Set elapsed timestamp if the original timestamp is missing.
*/
void setElapsedTimestampNs(int64_t timestampNs) {
@@ -197,39 +144,184 @@ public:
return &mValues;
}
+ // Default value = false
+ inline bool shouldTruncateTimestamp() const {
+ return mTruncateTimestamp;
+ }
+
+ // Returns the index of the uid field within the FieldValues vector if the
+ // uid exists. If there is no uid field, returns -1.
+ //
+ // If the index within the atom definition is desired, do the following:
+ // int vectorIndex = LogEvent.getUidFieldIndex();
+ // if (vectorIndex != -1) {
+ // FieldValue& v = LogEvent.getValues()[vectorIndex];
+ // int atomIndex = v.mField.getPosAtDepth(0);
+ // }
+ // Note that atomIndex is 1-indexed.
+ inline int getUidFieldIndex() {
+ return static_cast<int>(mUidFieldIndex);
+ }
+
+ // Returns whether this LogEvent has an AttributionChain.
+ // If it does and indexRange is not a nullptr, populate indexRange with the start and end index
+ // of the AttributionChain within mValues.
+ bool hasAttributionChain(std::pair<int, int>* indexRange = nullptr) const;
+
+ // Returns the index of the exclusive state field within the FieldValues vector if
+ // an exclusive state exists. If there is no exclusive state field, returns -1.
+ //
+ // If the index within the atom definition is desired, do the following:
+ // int vectorIndex = LogEvent.getExclusiveStateFieldIndex();
+ // if (vectorIndex != -1) {
+ // FieldValue& v = LogEvent.getValues()[vectorIndex];
+ // int atomIndex = v.mField.getPosAtDepth(0);
+ // }
+ // Note that atomIndex is 1-indexed.
+ inline int getExclusiveStateFieldIndex() const {
+ return static_cast<int>(mExclusiveStateFieldIndex);
+ }
+
+ // If a reset state is not sent in the StatsEvent, returns -1. Note that a
+ // reset state is sent if and only if a reset should be triggered.
+ inline int getResetState() const {
+ return mResetState;
+ }
+
inline LogEvent makeCopy() {
return LogEvent(*this);
}
+ template <class T>
+ status_t updateValue(size_t key, T& value, Type type) {
+ int field = getSimpleField(key);
+ for (auto& fieldValue : mValues) {
+ if (fieldValue.mField.getField() == field) {
+ if (fieldValue.mValue.getType() == type) {
+ fieldValue.mValue = Value(value);
+ return OK;
+ } else {
+ return BAD_TYPE;
+ }
+ }
+ }
+ return BAD_INDEX;
+ }
+
+ bool isValid() const {
+ return mValid;
+ }
+
private:
/**
* Only use this if copy is absolutely needed.
*/
- LogEvent(const LogEvent&);
+ LogEvent(const LogEvent&) = default;
+
+ void parseInt32(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseInt64(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseString(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseFloat(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseBool(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseByteArray(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseAttributionChain(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+
+ void parseAnnotations(uint8_t numAnnotations, int firstUidInChainIndex = -1);
+ void parseIsUidAnnotation(uint8_t annotationType);
+ void parseTruncateTimestampAnnotation(uint8_t annotationType);
+ void parsePrimaryFieldAnnotation(uint8_t annotationType);
+ void parsePrimaryFieldFirstUidAnnotation(uint8_t annotationType, int firstUidInChainIndex);
+ void parseExclusiveStateAnnotation(uint8_t annotationType);
+ void parseTriggerStateResetAnnotation(uint8_t annotationType);
+ void parseStateNestedAnnotation(uint8_t annotationType);
+ bool checkPreviousValueType(Type expected);
+
+ /**
+ * The below two variables are only valid during the execution of
+ * parseBuffer. There are no guarantees about the state of these variables
+ * before/after.
+ */
+ uint8_t* mBuf;
+ uint32_t mRemainingLen; // number of valid bytes left in the buffer being parsed
+
+ bool mValid = true; // stores whether the event we received from the socket is valid
/**
- * Parses a log_msg into a LogEvent object.
+ * Side-effects:
+ * If there is enough space in buffer to read value of type T
+ * - move mBuf past the value that was just read
+ * - decrement mRemainingLen by size of T
+ * Else
+ * - set mValid to false
*/
- void init(android_log_context context);
+ template <class T>
+ T readNextValue() {
+ T value;
+ if (mRemainingLen < sizeof(T)) {
+ mValid = false;
+ value = 0; // all primitive types can successfully cast 0
+ } else {
+ // When alignof(T) == 1, hopefully the compiler can optimize away
+ // this conditional as always true.
+ if ((reinterpret_cast<uintptr_t>(mBuf) % alignof(T)) == 0) {
+ // We're properly aligned, and can safely make this assignment.
+ value = *((T*)mBuf);
+ } else {
+ // We need to use memcpy. It's slower, but safe.
+ memcpy(&value, mBuf, sizeof(T));
+ }
+ mBuf += sizeof(T);
+ mRemainingLen -= sizeof(T);
+ }
+ return value;
+ }
+
+ template <class T>
+ void addToValues(int32_t* pos, int32_t depth, T& value, bool* last) {
+ Field f = Field(mTagId, pos, depth);
+ // do not decorate last position at depth 0
+ for (int i = 1; i < depth; i++) {
+ if (last[i]) f.decorateLastPos(i);
+ }
+
+ Value v = Value(value);
+ mValues.push_back(FieldValue(f, v));
+ }
+
+ uint8_t getTypeId(uint8_t typeInfo);
+ uint8_t getNumAnnotations(uint8_t typeInfo);
// The items are naturally sorted in DFS order as we read them. this allows us to do fast
// matching.
std::vector<FieldValue> mValues;
- // This field is used when statsD wants to create log event object and write fields to it. After
- // calling init() function, this object would be destroyed to save memory usage.
- // When the log event is created from log msg, this field is never initiated.
- android_log_context mContext = NULL;
-
// The timestamp set by the logd.
int64_t mLogdTimestampNs;
// The elapsed timestamp set by statsd log writer.
int64_t mElapsedTimestampNs;
- int mTagId;
+ // The atom tag of the event (defaults to 0 if client does not
+ // appropriately set the atom id).
+ int mTagId = 0;
+
+ // The uid of the logging client (defaults to -1).
+ int32_t mLogUid = -1;
+
+ // The pid of the logging client (defaults to -1).
+ int32_t mLogPid = -1;
+
+ // Annotations
+ bool mTruncateTimestamp = false;
+ int mResetState = -1;
- uint32_t mLogUid;
+ // Indexes within the FieldValue vector can be stored in 7 bits because
+ // that's the assumption enforced by the encoding used in FieldValue.
+ int8_t mUidFieldIndex = -1;
+ int8_t mAttributionChainStartIndex = -1;
+ int8_t mAttributionChainEndIndex = -1;
+ int8_t mExclusiveStateFieldIndex = -1;
};
void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds, std::vector<uint8_t>* protoOut);
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index 739c5977a2d0..cd9c4e5b947b 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -20,10 +20,9 @@
#include "StatsService.h"
#include "socket/StatsSocketListener.h"
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
-#include <hidl/HidlTransportSupport.h>
+#include <android/binder_interface_utils.h>
+#include <android/binder_process.h>
+#include <android/binder_manager.h>
#include <utils/Looper.h>
#include <stdio.h>
@@ -33,31 +32,35 @@
using namespace android;
using namespace android::os::statsd;
-
-/**
- * Thread function data.
- */
-struct log_reader_thread_data {
- sp<StatsService> service;
-};
-
-
-sp<StatsService> gStatsService = nullptr;
-
-void sigHandler(int sig) {
- if (gStatsService != nullptr) {
- gStatsService->Terminate();
+using ::ndk::SharedRefBase;
+using std::shared_ptr;
+using std::make_shared;
+
+shared_ptr<StatsService> gStatsService = nullptr;
+sp<StatsSocketListener> gSocketListener = nullptr;
+
+void signalHandler(int sig) {
+ if (sig == SIGPIPE) {
+ // ShellSubscriber uses SIGPIPE as a signal to detect the end of the
+ // client process. Don't prematurely exit(1) here. Instead, ignore the
+ // signal and allow the write call to return EPIPE.
+ ALOGI("statsd received SIGPIPE. Ignoring signal.");
+ return;
}
+
+ if (gSocketListener != nullptr) gSocketListener->stopListener();
+ if (gStatsService != nullptr) gStatsService->Terminate();
ALOGW("statsd terminated on receiving signal %d.", sig);
exit(1);
}
-void registerSigHandler()
+void registerSignalHandlers()
{
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
- sa.sa_handler = sigHandler;
+ sa.sa_handler = signalHandler;
+ sigaction(SIGPIPE, &sa, nullptr);
sigaction(SIGHUP, &sa, nullptr);
sigaction(SIGINT, &sa, nullptr);
sigaction(SIGQUIT, &sa, nullptr);
@@ -69,35 +72,33 @@ int main(int /*argc*/, char** /*argv*/) {
sp<Looper> looper(Looper::prepare(0 /* opts */));
// Set up the binder
- sp<ProcessState> ps(ProcessState::self());
- ps->setThreadPoolMaxThreadCount(9);
- ps->startThreadPool();
- ps->giveThreadPoolName();
- IPCThreadState::self()->disableBackgroundScheduling(true);
+ ABinderProcess_setThreadPoolMaxThreadCount(9);
+ ABinderProcess_startThreadPool();
std::shared_ptr<LogEventQueue> eventQueue =
std::make_shared<LogEventQueue>(2000 /*buffer limit. Buffer is NOT pre-allocated*/);
// Create the service
- gStatsService = new StatsService(looper, eventQueue);
- if (defaultServiceManager()->addService(String16("stats"), gStatsService, false,
- IServiceManager::DUMP_FLAG_PRIORITY_NORMAL | IServiceManager::DUMP_FLAG_PROTO)
- != 0) {
+ gStatsService = SharedRefBase::make<StatsService>(looper, eventQueue);
+ // TODO(b/149582373): Set DUMP_FLAG_PROTO once libbinder_ndk supports
+ // setting dumpsys priorities.
+ binder_status_t status = AServiceManager_addService(gStatsService->asBinder().get(), "stats");
+ if (status != STATUS_OK) {
ALOGE("Failed to add service as AIDL service");
return -1;
}
- registerSigHandler();
+ registerSignalHandlers();
gStatsService->sayHiToStatsCompanion();
gStatsService->Startup();
- sp<StatsSocketListener> socketListener = new StatsSocketListener(eventQueue);
+ gSocketListener = new StatsSocketListener(eventQueue);
ALOGI("Statsd starts to listen to socket.");
// Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
- if (socketListener->startListener(600)) {
+ if (gSocketListener->startListener(600)) {
exit(1);
}
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index 2cbe2e96f142..2b4c6a3cbf1e 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -81,18 +81,17 @@ bool combinationMatch(const vector<int>& children, const LogicalOperation& opera
return matched;
}
-bool tryMatchString(const UidMap& uidMap, const Field& field, const Value& value,
- const string& str_match) {
- if (isAttributionUidField(field, value)) {
- int uid = value.int_value;
+bool tryMatchString(const UidMap& uidMap, const FieldValue& fieldValue, const string& str_match) {
+ if (isAttributionUidField(fieldValue) || isUidField(fieldValue)) {
+ int uid = fieldValue.mValue.int_value;
auto aidIt = UidMap::sAidToUidMapping.find(str_match);
if (aidIt != UidMap::sAidToUidMapping.end()) {
return ((int)aidIt->second) == uid;
}
std::set<string> packageNames = uidMap.getAppNamesFromUid(uid, true /* normalize*/);
return packageNames.find(str_match) != packageNames.end();
- } else if (value.getType() == STRING) {
- return value.str_value == str_match;
+ } else if (fieldValue.mValue.getType() == STRING) {
+ return fieldValue.mValue.str_value == str_match;
}
return false;
}
@@ -228,8 +227,7 @@ bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher,
}
case FieldValueMatcher::ValueMatcherCase::kEqString: {
for (int i = start; i < end; i++) {
- if (tryMatchString(uidMap, values[i].mField, values[i].mValue,
- matcher.eq_string())) {
+ if (tryMatchString(uidMap, values[i], matcher.eq_string())) {
return true;
}
}
@@ -240,7 +238,7 @@ bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher,
for (int i = start; i < end; i++) {
bool notEqAll = true;
for (const auto& str : str_list.str_value()) {
- if (tryMatchString(uidMap, values[i].mField, values[i].mValue, str)) {
+ if (tryMatchString(uidMap, values[i], str)) {
notEqAll = false;
break;
}
@@ -255,7 +253,7 @@ bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher,
const auto& str_list = matcher.eq_any_string();
for (int i = start; i < end; i++) {
for (const auto& str : str_list.str_value()) {
- if (tryMatchString(uidMap, values[i].mField, values[i].mValue, str)) {
+ if (tryMatchString(uidMap, values[i], str)) {
return true;
}
}
@@ -357,9 +355,10 @@ bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher,
bool matchesSimple(const UidMap& uidMap, const SimpleAtomMatcher& simpleMatcher,
const LogEvent& event) {
- if (simpleMatcher.field_value_matcher_size() <= 0) {
- return event.GetTagId() == simpleMatcher.atom_id();
+ if (event.GetTagId() != simpleMatcher.atom_id()) {
+ return false;
}
+
for (const auto& matcher : simpleMatcher.field_value_matcher()) {
if (!matchesSimple(uidMap, matcher, event.getValues(), 0, event.getValues().size(), 0)) {
return false;
diff --git a/cmds/statsd/src/metadata_util.cpp b/cmds/statsd/src/metadata_util.cpp
new file mode 100644
index 000000000000..27ee59b36242
--- /dev/null
+++ b/cmds/statsd/src/metadata_util.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2020 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 "FieldValue.h"
+#include "metadata_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using google::protobuf::RepeatedPtrField;
+
+void writeValueToProto(metadata::FieldValue* metadataFieldValue, const Value& value) {
+ std::string storage_value;
+ switch (value.getType()) {
+ case INT:
+ metadataFieldValue->set_value_int(value.int_value);
+ break;
+ case LONG:
+ metadataFieldValue->set_value_long(value.long_value);
+ break;
+ case FLOAT:
+ metadataFieldValue->set_value_float(value.float_value);
+ break;
+ case DOUBLE:
+ metadataFieldValue->set_value_double(value.double_value);
+ break;
+ case STRING:
+ metadataFieldValue->set_value_str(value.str_value.c_str());
+ break;
+ case STORAGE: // byte array
+ storage_value = ((char*) value.storage_value.data());
+ metadataFieldValue->set_value_storage(storage_value);
+ break;
+ default:
+ break;
+ }
+}
+
+void writeMetricDimensionKeyToMetadataDimensionKey(
+ const MetricDimensionKey& metricKey,
+ metadata::MetricDimensionKey* metadataMetricKey) {
+ for (const FieldValue& fieldValue : metricKey.getDimensionKeyInWhat().getValues()) {
+ metadata::FieldValue* metadataFieldValue = metadataMetricKey->add_dimension_key_in_what();
+ metadata::Field* metadataField = metadataFieldValue->mutable_field();
+ metadataField->set_tag(fieldValue.mField.getTag());
+ metadataField->set_field(fieldValue.mField.getField());
+ writeValueToProto(metadataFieldValue, fieldValue.mValue);
+ }
+
+ for (const FieldValue& fieldValue : metricKey.getStateValuesKey().getValues()) {
+ metadata::FieldValue* metadataFieldValue = metadataMetricKey->add_state_values_key();
+ metadata::Field* metadataField = metadataFieldValue->mutable_field();
+ metadataField->set_tag(fieldValue.mField.getTag());
+ metadataField->set_field(fieldValue.mField.getField());
+ writeValueToProto(metadataFieldValue, fieldValue.mValue);
+ }
+}
+
+void writeFieldValuesFromMetadata(
+ const RepeatedPtrField<metadata::FieldValue>& repeatedFieldValueList,
+ std::vector<FieldValue>* fieldValues) {
+ for (const metadata::FieldValue& metadataFieldValue : repeatedFieldValueList) {
+ Field field(metadataFieldValue.field().tag(), metadataFieldValue.field().field());
+ Value value;
+ switch (metadataFieldValue.value_case()) {
+ case metadata::FieldValue::ValueCase::kValueInt:
+ value = Value(metadataFieldValue.value_int());
+ break;
+ case metadata::FieldValue::ValueCase::kValueLong:
+ value = Value(metadataFieldValue.value_long());
+ break;
+ case metadata::FieldValue::ValueCase::kValueFloat:
+ value = Value(metadataFieldValue.value_float());
+ break;
+ case metadata::FieldValue::ValueCase::kValueDouble:
+ value = Value(metadataFieldValue.value_double());
+ break;
+ case metadata::FieldValue::ValueCase::kValueStr:
+ value = Value(metadataFieldValue.value_str());
+ break;
+ case metadata::FieldValue::ValueCase::kValueStorage:
+ value = Value(metadataFieldValue.value_storage());
+ break;
+ default:
+ break;
+ }
+ FieldValue fieldValue(field, value);
+ fieldValues->emplace_back(field, value);
+ }
+}
+
+MetricDimensionKey loadMetricDimensionKeyFromProto(
+ const metadata::MetricDimensionKey& metricDimensionKey) {
+ std::vector<FieldValue> dimKeyInWhatFieldValues;
+ writeFieldValuesFromMetadata(metricDimensionKey.dimension_key_in_what(),
+ &dimKeyInWhatFieldValues);
+ std::vector<FieldValue> stateValuesFieldValues;
+ writeFieldValuesFromMetadata(metricDimensionKey.state_values_key(), &stateValuesFieldValues);
+
+ HashableDimensionKey dimKeyInWhat(dimKeyInWhatFieldValues);
+ HashableDimensionKey stateValues(stateValuesFieldValues);
+ MetricDimensionKey metricKey(dimKeyInWhat, stateValues);
+ return metricKey;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android \ No newline at end of file
diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.h b/cmds/statsd/src/metadata_util.h
index f650fccc447e..84a39ff872b5 100644
--- a/cmds/statsd/src/external/ResourceHealthManagerPuller.h
+++ b/cmds/statsd/src/metadata_util.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -13,27 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include "HashableDimensionKey.h"
-#pragma once
-
-#include <utils/String16.h>
-#include "StatsPuller.h"
+#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h" // AlertMetadata
namespace android {
namespace os {
namespace statsd {
-/**
- * Reads Ihealth.hal
- */
-class ResourceHealthManagerPuller : public StatsPuller {
-public:
- explicit ResourceHealthManagerPuller(int tagId);
+void writeMetricDimensionKeyToMetadataDimensionKey(const MetricDimensionKey& metricKey,
+ metadata::MetricDimensionKey* metadataMetricKey);
-private:
- bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
-};
+MetricDimensionKey loadMetricDimensionKeyFromProto(
+ const metadata::MetricDimensionKey& metricDimensionKey);
} // namespace statsd
} // namespace os
-} // namespace android
+} // namespace android \ No newline at end of file
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index c023e6f77e7c..573961276e5b 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -18,13 +18,15 @@
#include "Log.h"
#include "CountMetricProducer.h"
-#include "guardrail/StatsdStats.h"
-#include "stats_util.h"
-#include "stats_log_util.h"
+#include <inttypes.h>
#include <limits.h>
#include <stdlib.h>
+#include "guardrail/StatsdStats.h"
+#include "stats_log_util.h"
+#include "stats_util.h"
+
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_FLOAT;
@@ -37,6 +39,7 @@ using std::map;
using std::string;
using std::unordered_map;
using std::vector;
+using std::shared_ptr;
namespace android {
namespace os {
@@ -48,28 +51,31 @@ const int FIELD_ID_COUNT_METRICS = 5;
const int FIELD_ID_TIME_BASE = 9;
const int FIELD_ID_BUCKET_SIZE = 10;
const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
-const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
const int FIELD_ID_IS_ACTIVE = 14;
// for CountMetricDataWrapper
const int FIELD_ID_DATA = 1;
// for CountMetricData
const int FIELD_ID_DIMENSION_IN_WHAT = 1;
-const int FIELD_ID_DIMENSION_IN_CONDITION = 2;
+const int FIELD_ID_SLICE_BY_STATE = 6;
const int FIELD_ID_BUCKET_INFO = 3;
const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
-const int FIELD_ID_DIMENSION_LEAF_IN_CONDITION = 5;
// for CountBucketInfo
const int FIELD_ID_COUNT = 3;
const int FIELD_ID_BUCKET_NUM = 4;
const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
-CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric& metric,
- const int conditionIndex,
- const sp<ConditionWizard>& wizard,
- const int64_t timeBaseNs, const int64_t startTimeNs)
- : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard) {
+CountMetricProducer::CountMetricProducer(
+ const ConfigKey& key, const CountMetric& metric, const int conditionIndex,
+ const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+ const int64_t timeBaseNs, const int64_t startTimeNs,
+ const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+ const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+ const vector<int>& slicedStateAtoms,
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
+ : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard,
+ eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap) {
if (metric.has_bucket()) {
mBucketSizeNs =
TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
@@ -82,12 +88,7 @@ CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric
mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
}
- mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()) ||
- HasPositionALL(metric.dimensions_in_condition());
-
- if (metric.has_dimensions_in_condition()) {
- translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition);
- }
+ mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what());
if (metric.links().size() > 0) {
for (const auto& link : metric.links()) {
@@ -100,7 +101,13 @@ CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric
mConditionSliced = true;
}
- mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
+ for (const auto& stateLink : metric.state_link()) {
+ Metric2State ms;
+ ms.stateAtomId = stateLink.state_atom_id();
+ translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields);
+ translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields);
+ mMetric2StateLinks.push_back(ms);
+ }
flushIfNeededLocked(startTimeNs);
// Adjust start for partial bucket
@@ -114,6 +121,14 @@ CountMetricProducer::~CountMetricProducer() {
VLOG("~CountMetricProducer() called");
}
+void CountMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
+ const HashableDimensionKey& primaryKey,
+ const FieldValue& oldState, const FieldValue& newState) {
+ VLOG("CountMetric %lld onStateChanged time %lld, State%d, key %s, %d -> %d",
+ (long long)mMetricId, (long long)eventTimeNs, atomId, primaryKey.toString().c_str(),
+ oldState.mValue.int_value, newState.mValue.int_value);
+}
+
void CountMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
if (mCurrentSlicedCounter == nullptr ||
mCurrentSlicedCounter->size() == 0) {
@@ -124,10 +139,9 @@ void CountMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
(unsigned long)mCurrentSlicedCounter->size());
if (verbose) {
for (const auto& it : *mCurrentSlicedCounter) {
- fprintf(out, "\t(what)%s\t(condition)%s %lld\n",
- it.first.getDimensionKeyInWhat().toString().c_str(),
- it.first.getDimensionKeyInCondition().toString().c_str(),
- (unsigned long long)it.second);
+ fprintf(out, "\t(what)%s\t(state)%s %lld\n",
+ it.first.getDimensionKeyInWhat().toString().c_str(),
+ it.first.getStateValuesKey().toString().c_str(), (unsigned long long)it.second);
}
}
}
@@ -171,13 +185,6 @@ void CountMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
protoOutput->end(dimenPathToken);
}
- if (!mDimensionsInCondition.empty()) {
- uint64_t dimenPathToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_CONDITION);
- writeDimensionPathToProto(mDimensionsInCondition, protoOutput);
- protoOutput->end(dimenPathToken);
- }
-
}
uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS);
@@ -195,22 +202,16 @@ void CountMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
protoOutput->end(dimensionToken);
-
- if (dimensionKey.hasDimensionKeyInCondition()) {
- uint64_t dimensionInConditionToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
- writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(),
- str_set, protoOutput);
- protoOutput->end(dimensionInConditionToken);
- }
} else {
writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
- if (dimensionKey.hasDimensionKeyInCondition()) {
- writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInCondition(),
- FIELD_ID_DIMENSION_LEAF_IN_CONDITION,
- str_set, protoOutput);
- }
+ }
+ // Then fill slice_by_state.
+ for (auto state : dimensionKey.getStateValuesKey().getValues()) {
+ uint64_t stateToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+ FIELD_ID_SLICE_BY_STATE);
+ writeStateToProto(state, protoOutput);
+ protoOutput->end(stateToken);
}
// Then fill bucket_info (CountBucketInfo).
for (const auto& bucket : counter.second) {
@@ -266,6 +267,7 @@ bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
ALOGE("CountMetric %lld dropping data for dimension key %s",
(long long)mMetricId, newKey.toString().c_str());
+ StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId);
return true;
}
}
@@ -275,12 +277,12 @@ bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
void CountMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition,
- const LogEvent& event) {
+ const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+ const map<int, HashableDimensionKey>& statePrimaryKeys) {
int64_t eventTimeNs = event.GetElapsedTimestampNs();
flushIfNeededLocked(eventTimeNs);
- if (condition == false) {
+ if (!condition) {
return;
}
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index b4a910c6f410..f05fb061ccc1 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -17,15 +17,16 @@
#ifndef COUNT_METRIC_PRODUCER_H
#define COUNT_METRIC_PRODUCER_H
-#include <unordered_map>
-
#include <android/util/ProtoOutputStream.h>
#include <gtest/gtest_prod.h>
-#include "../anomaly/AnomalyTracker.h"
-#include "../condition/ConditionTracker.h"
-#include "../matchers/matcher_util.h"
+
+#include <unordered_map>
+
#include "MetricProducer.h"
+#include "anomaly/AnomalyTracker.h"
+#include "condition/ConditionTracker.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "matchers/matcher_util.h"
#include "stats_util.h"
namespace android {
@@ -40,17 +41,27 @@ struct CountBucket {
class CountMetricProducer : public MetricProducer {
public:
- CountMetricProducer(const ConfigKey& key, const CountMetric& countMetric,
- const int conditionIndex, const sp<ConditionWizard>& wizard,
- const int64_t timeBaseNs, const int64_t startTimeNs);
+ CountMetricProducer(
+ const ConfigKey& key, const CountMetric& countMetric, const int conditionIndex,
+ const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+ const int64_t timeBaseNs, const int64_t startTimeNs,
+ const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
+ const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+ eventDeactivationMap = {},
+ const vector<int>& slicedStateAtoms = {},
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
virtual ~CountMetricProducer();
+ void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
+ const HashableDimensionKey& primaryKey, const FieldValue& oldState,
+ const FieldValue& newState) override;
+
protected:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition,
- const LogEvent& event) override;
+ const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+ const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
private:
@@ -99,9 +110,11 @@ private:
FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition);
FRIEND_TEST(CountMetricProducerTest, TestEventsWithSlicedCondition);
FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced);
- FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgrade);
- FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket);
FRIEND_TEST(CountMetricProducerTest, TestFirstBucket);
+ FRIEND_TEST(CountMetricProducerTest, TestOneWeekTimeUnit);
+
+ FRIEND_TEST(CountMetricProducerTest_PartialBucket, TestSplitInCurrentBucket);
+ FRIEND_TEST(CountMetricProducerTest_PartialBucket, TestSplitInNextBucket);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 96fbf7fb5ebe..e9b043876d3d 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -36,6 +36,7 @@ using android::util::ProtoOutputStream;
using std::string;
using std::unordered_map;
using std::vector;
+using std::shared_ptr;
namespace android {
namespace os {
@@ -47,30 +48,32 @@ const int FIELD_ID_DURATION_METRICS = 6;
const int FIELD_ID_TIME_BASE = 9;
const int FIELD_ID_BUCKET_SIZE = 10;
const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
-const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
const int FIELD_ID_IS_ACTIVE = 14;
// for DurationMetricDataWrapper
const int FIELD_ID_DATA = 1;
// for DurationMetricData
const int FIELD_ID_DIMENSION_IN_WHAT = 1;
-const int FIELD_ID_DIMENSION_IN_CONDITION = 2;
const int FIELD_ID_BUCKET_INFO = 3;
const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
-const int FIELD_ID_DIMENSION_LEAF_IN_CONDITION = 5;
+const int FIELD_ID_SLICE_BY_STATE = 6;
// for DurationBucketInfo
const int FIELD_ID_DURATION = 3;
const int FIELD_ID_BUCKET_NUM = 4;
const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
-DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const DurationMetric& metric,
- const int conditionIndex, const size_t startIndex,
- const size_t stopIndex, const size_t stopAllIndex,
- const bool nesting,
- const sp<ConditionWizard>& wizard,
- const FieldMatcher& internalDimensions,
- const int64_t timeBaseNs, const int64_t startTimeNs)
- : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
+DurationMetricProducer::DurationMetricProducer(
+ const ConfigKey& key, const DurationMetric& metric, const int conditionIndex,
+ const vector<ConditionState>& initialConditionCache, const size_t startIndex,
+ const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
+ const sp<ConditionWizard>& wizard, const FieldMatcher& internalDimensions,
+ const int64_t timeBaseNs, const int64_t startTimeNs,
+ const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+ const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+ const vector<int>& slicedStateAtoms,
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
+ : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard,
+ eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap),
mAggregationType(metric.aggregation_type()),
mStartIndex(startIndex),
mStopIndex(stopIndex),
@@ -100,12 +103,7 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat
ALOGE("Position ANY in dimension_in_what not supported.");
}
- if (metric.has_dimensions_in_condition()) {
- translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition);
- }
-
- mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()) ||
- HasPositionALL(metric.dimensions_in_condition());
+ mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what());
if (metric.links().size() > 0) {
for (const auto& link : metric.links()) {
@@ -115,19 +113,23 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat
translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
mMetric2ConditionLinks.push_back(mc);
}
+ mConditionSliced = true;
}
- mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
mUnSlicedPartCondition = ConditionState::kUnknown;
+ for (const auto& stateLink : metric.state_link()) {
+ Metric2State ms;
+ ms.stateAtomId = stateLink.state_atom_id();
+ translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields);
+ translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields);
+ mMetric2StateLinks.push_back(ms);
+ }
+
mUseWhatDimensionAsInternalDimension = equalDimensions(mDimensionsInWhat, mInternalDimensions);
- if (mWizard != nullptr && mConditionTrackerIndex >= 0) {
- mSameConditionDimensionsInTracker =
- mWizard->equalOutputDimensions(mConditionTrackerIndex, mDimensionsInCondition);
- if (mMetric2ConditionLinks.size() == 1) {
- mHasLinksToAllConditionDimensionsInTracker =
- mWizard->equalOutputDimensions(mConditionTrackerIndex,
- mMetric2ConditionLinks.begin()->conditionFields);
- }
+ if (mWizard != nullptr && mConditionTrackerIndex >= 0 &&
+ mMetric2ConditionLinks.size() == 1) {
+ mHasLinksToAllConditionDimensionsInTracker = mWizard->equalOutputDimensions(
+ mConditionTrackerIndex, mMetric2ConditionLinks.begin()->conditionFields);
}
flushIfNeededLocked(startTimeNs);
// Adjust start for partial bucket
@@ -158,33 +160,58 @@ sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker(
return anomalyTracker;
}
+void DurationMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
+ const HashableDimensionKey& primaryKey,
+ const FieldValue& oldState,
+ const FieldValue& newState) {
+ // Check if this metric has a StateMap. If so, map the new state value to
+ // the correct state group id.
+ FieldValue newStateCopy = newState;
+ mapStateValue(atomId, &newStateCopy);
+
+ flushIfNeededLocked(eventTimeNs);
+
+ // Each duration tracker is mapped to a different whatKey (a set of values from the
+ // dimensionsInWhat fields). We notify all trackers iff the primaryKey field values from the
+ // state change event are a subset of the tracker's whatKey field values.
+ //
+ // Ex. For a duration metric dimensioned on uid and tag:
+ // DurationTracker1 whatKey = uid: 1001, tag: 1
+ // DurationTracker2 whatKey = uid: 1002, tag 1
+ //
+ // If the state change primaryKey = uid: 1001, we only notify DurationTracker1 of a state
+ // change.
+ for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
+ if (!containsLinkedStateValues(whatIt.first, primaryKey, mMetric2StateLinks, atomId)) {
+ continue;
+ }
+ whatIt.second->onStateChanged(eventTimeNs, atomId, newStateCopy);
+ }
+}
+
unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
const MetricDimensionKey& eventKey) const {
switch (mAggregationType) {
case DurationMetric_AggregationType_SUM:
return make_unique<OringDurationTracker>(
- mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
- mDimensionsInCondition, mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum,
- mTimeBaseNs, mBucketSizeNs, mConditionSliced,
- mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
+ mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
+ mCurrentBucketStartTimeNs, mCurrentBucketNum, mTimeBaseNs, mBucketSizeNs,
+ mConditionSliced, mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
case DurationMetric_AggregationType_MAX_SPARSE:
return make_unique<MaxDurationTracker>(
- mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
- mDimensionsInCondition, mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum,
- mTimeBaseNs, mBucketSizeNs, mConditionSliced,
- mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
+ mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
+ mCurrentBucketStartTimeNs, mCurrentBucketNum, mTimeBaseNs, mBucketSizeNs,
+ mConditionSliced, mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
}
}
// SlicedConditionChange optimization case 1:
// 1. If combination condition, logical operation is AND, only one sliced child predicate.
-// 2. No condition in dimension
-// 3. The links covers all dimension fields in the sliced child condition predicate.
+// 2. The links covers all dimension fields in the sliced child condition predicate.
void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(bool condition,
const int64_t eventTime) {
if (mMetric2ConditionLinks.size() != 1 ||
- !mHasLinksToAllConditionDimensionsInTracker ||
- !mDimensionsInCondition.empty()) {
+ !mHasLinksToAllConditionDimensionsInTracker) {
return;
}
@@ -213,15 +240,11 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(bool conditio
mWizard->getTrueSlicedDimensions(mConditionTrackerIndex, &trueConditionDimensions);
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
HashableDimensionKey linkedConditionDimensionKey;
- getDimensionForCondition(whatIt.first.getValues(),
- mMetric2ConditionLinks[0],
+ getDimensionForCondition(whatIt.first.getValues(), mMetric2ConditionLinks[0],
&linkedConditionDimensionKey);
if (trueConditionDimensions.find(linkedConditionDimensionKey) !=
trueConditionDimensions.end()) {
- for (auto& condIt : whatIt.second) {
- condIt.second->onConditionChanged(
- currentUnSlicedPartCondition, eventTime);
- }
+ whatIt.second->onConditionChanged(currentUnSlicedPartCondition, eventTime);
}
}
} else {
@@ -229,109 +252,15 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(bool conditio
if (currentUnSlicedPartCondition) {
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
HashableDimensionKey linkedConditionDimensionKey;
- getDimensionForCondition(whatIt.first.getValues(),
- mMetric2ConditionLinks[0],
+ getDimensionForCondition(whatIt.first.getValues(), mMetric2ConditionLinks[0],
&linkedConditionDimensionKey);
if (dimensionsChangedToTrue->find(linkedConditionDimensionKey) !=
dimensionsChangedToTrue->end()) {
- for (auto& condIt : whatIt.second) {
- condIt.second->onConditionChanged(true, eventTime);
- }
+ whatIt.second->onConditionChanged(true, eventTime);
}
if (dimensionsChangedToFalse->find(linkedConditionDimensionKey) !=
dimensionsChangedToFalse->end()) {
- for (auto& condIt : whatIt.second) {
- condIt.second->onConditionChanged(false, eventTime);
- }
- }
- }
- }
- }
-}
-
-
-// SlicedConditionChange optimization case 2:
-// 1. If combination condition, logical operation is AND, only one sliced child predicate.
-// 2. Has dimensions_in_condition and it equals to the output dimensions of the sliced predicate.
-void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt2(bool condition,
- const int64_t eventTime) {
- if (mMetric2ConditionLinks.size() > 1 || !mSameConditionDimensionsInTracker) {
- return;
- }
-
- auto dimensionsChangedToTrue = mWizard->getChangedToTrueDimensions(mConditionTrackerIndex);
- auto dimensionsChangedToFalse = mWizard->getChangedToFalseDimensions(mConditionTrackerIndex);
-
- bool currentUnSlicedPartCondition = true;
- if (!mWizard->IsSimpleCondition(mConditionTrackerIndex)) {
- ConditionState unslicedPartState =
- mWizard->getUnSlicedPartConditionState(mConditionTrackerIndex);
- // When the unsliced part is still false, return directly.
- if (mUnSlicedPartCondition == ConditionState::kFalse &&
- unslicedPartState == ConditionState::kFalse) {
- return;
- }
- mUnSlicedPartCondition = unslicedPartState;
- currentUnSlicedPartCondition = mUnSlicedPartCondition > 0;
- }
-
- const std::set<HashableDimensionKey>* trueDimensionsToProcess = nullptr;
- const std::set<HashableDimensionKey>* falseDimensionsToProcess = nullptr;
-
- std::set<HashableDimensionKey> currentTrueConditionDimensions;
- if (dimensionsChangedToTrue == nullptr || dimensionsChangedToFalse == nullptr ||
- (dimensionsChangedToTrue->empty() && dimensionsChangedToFalse->empty())) {
- mWizard->getTrueSlicedDimensions(mConditionTrackerIndex, &currentTrueConditionDimensions);
- trueDimensionsToProcess = &currentTrueConditionDimensions;
- } else if (currentUnSlicedPartCondition) {
- // Handles the condition change from the sliced predicate. If the unsliced condition state
- // is not true, not need to do anything.
- trueDimensionsToProcess = dimensionsChangedToTrue;
- falseDimensionsToProcess = dimensionsChangedToFalse;
- }
-
- if (trueDimensionsToProcess == nullptr && falseDimensionsToProcess == nullptr) {
- return;
- }
-
- for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- if (falseDimensionsToProcess != nullptr) {
- for (const auto& changedDim : *falseDimensionsToProcess) {
- auto condIt = whatIt.second.find(changedDim);
- if (condIt != whatIt.second.end()) {
- condIt->second->onConditionChanged(false, eventTime);
- }
- }
- }
- if (trueDimensionsToProcess != nullptr) {
- HashableDimensionKey linkedConditionDimensionKey;
- if (!trueDimensionsToProcess->empty() && mMetric2ConditionLinks.size() == 1) {
- getDimensionForCondition(whatIt.first.getValues(),
- mMetric2ConditionLinks[0],
- &linkedConditionDimensionKey);
- }
- for (auto& trueDim : *trueDimensionsToProcess) {
- auto condIt = whatIt.second.find(trueDim);
- if (condIt != whatIt.second.end()) {
- condIt->second->onConditionChanged(
- currentUnSlicedPartCondition, eventTime);
- } else {
- if (mMetric2ConditionLinks.size() == 0 ||
- trueDim.contains(linkedConditionDimensionKey)) {
- if (!whatIt.second.empty()) {
- auto newEventKey = MetricDimensionKey(whatIt.first, trueDim);
- if (hitGuardRailLocked(newEventKey)) {
- continue;
- }
- unique_ptr<DurationTracker> newTracker =
- whatIt.second.begin()->second->clone(eventTime);
- if (newTracker != nullptr) {
- newTracker->setEventKey(newEventKey);
- newTracker->onConditionChanged(true, eventTime);
- whatIt.second[trueDim] = std::move(newTracker);
- }
- }
- }
+ whatIt.second->onConditionChanged(false, eventTime);
}
}
}
@@ -341,85 +270,14 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt2(bool conditio
void DurationMetricProducer::onSlicedConditionMayChangeInternalLocked(bool overallCondition,
const int64_t eventTimeNs) {
bool changeDimTrackable = mWizard->IsChangedDimensionTrackable(mConditionTrackerIndex);
- if (changeDimTrackable && mHasLinksToAllConditionDimensionsInTracker &&
- mDimensionsInCondition.empty()) {
+ if (changeDimTrackable && mHasLinksToAllConditionDimensionsInTracker) {
onSlicedConditionMayChangeLocked_opt1(overallCondition, eventTimeNs);
return;
}
- if (changeDimTrackable && mSameConditionDimensionsInTracker &&
- mMetric2ConditionLinks.size() <= 1) {
- onSlicedConditionMayChangeLocked_opt2(overallCondition, eventTimeNs);
- return;
- }
-
// Now for each of the on-going event, check if the condition has changed for them.
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- for (auto& pair : whatIt.second) {
- pair.second->onSlicedConditionMayChange(overallCondition, eventTimeNs);
- }
- }
-
- if (mDimensionsInCondition.empty()) {
- return;
- }
-
- if (mMetric2ConditionLinks.empty()) {
- std::unordered_set<HashableDimensionKey> conditionDimensionsKeySet;
- mWizard->getMetConditionDimension(mConditionTrackerIndex, mDimensionsInCondition,
- !mSameConditionDimensionsInTracker,
- &conditionDimensionsKeySet);
- for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- for (const auto& pair : whatIt.second) {
- conditionDimensionsKeySet.erase(pair.first);
- }
- }
- for (const auto& conditionDimension : conditionDimensionsKeySet) {
- for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- if (!whatIt.second.empty()) {
- auto newEventKey = MetricDimensionKey(whatIt.first, conditionDimension);
- if (hitGuardRailLocked(newEventKey)) {
- continue;
- }
- unique_ptr<DurationTracker> newTracker =
- whatIt.second.begin()->second->clone(eventTimeNs);
- if (newTracker != nullptr) {
- newTracker->setEventKey(MetricDimensionKey(newEventKey));
- newTracker->onSlicedConditionMayChange(overallCondition, eventTimeNs);
- whatIt.second[conditionDimension] = std::move(newTracker);
- }
- }
- }
- }
- } else {
- for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- ConditionKey conditionKey;
- for (const auto& link : mMetric2ConditionLinks) {
- getDimensionForCondition(whatIt.first.getValues(), link,
- &conditionKey[link.conditionId]);
- }
- std::unordered_set<HashableDimensionKey> conditionDimensionsKeys;
- mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
- !mSameConditionDimensionsInTracker,
- !mHasLinksToAllConditionDimensionsInTracker,
- &conditionDimensionsKeys);
-
- for (const auto& conditionDimension : conditionDimensionsKeys) {
- if (!whatIt.second.empty() &&
- whatIt.second.find(conditionDimension) == whatIt.second.end()) {
- auto newEventKey = MetricDimensionKey(whatIt.first, conditionDimension);
- if (hitGuardRailLocked(newEventKey)) {
- continue;
- }
- auto newTracker = whatIt.second.begin()->second->clone(eventTimeNs);
- if (newTracker != nullptr) {
- newTracker->setEventKey(newEventKey);
- newTracker->onSlicedConditionMayChange(overallCondition, eventTimeNs);
- whatIt.second[conditionDimension] = std::move(newTracker);
- }
- }
- }
- }
+ whatIt.second->onSlicedConditionMayChange(overallCondition, eventTimeNs);
}
}
@@ -453,18 +311,14 @@ void DurationMetricProducer::onActiveStateChangedLocked(const int64_t& eventTime
}
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- for (auto& pair : whatIt.second) {
- pair.second->onConditionChanged(mIsActive, eventTimeNs);
- }
+ whatIt.second->onConditionChanged(mIsActive, eventTimeNs);
}
} else if (mIsActive) {
flushIfNeededLocked(eventTimeNs);
onSlicedConditionMayChangeInternalLocked(mIsActive, eventTimeNs);
} else { // mConditionSliced == true && !mIsActive
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- for (auto& pair : whatIt.second) {
- pair.second->onConditionChanged(mIsActive, eventTimeNs);
- }
+ whatIt.second->onConditionChanged(mIsActive, eventTimeNs);
}
}
}
@@ -480,9 +334,7 @@ void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet,
flushIfNeededLocked(eventTime);
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- for (auto& pair : whatIt.second) {
- pair.second->onConditionChanged(conditionMet, eventTime);
- }
+ whatIt.second->onConditionChanged(conditionMet, eventTime);
}
}
@@ -526,12 +378,6 @@ void DurationMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
protoOutput->end(dimenPathToken);
}
- if (!mDimensionsInCondition.empty()) {
- uint64_t dimenPathToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_CONDITION);
- writeDimensionPathToProto(mDimensionsInCondition, protoOutput);
- protoOutput->end(dimenPathToken);
- }
}
uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS);
@@ -551,22 +397,16 @@ void DurationMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
protoOutput->end(dimensionToken);
-
- if (dimensionKey.hasDimensionKeyInCondition()) {
- uint64_t dimensionInConditionToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
- writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(),
- str_set, protoOutput);
- protoOutput->end(dimensionInConditionToken);
- }
} else {
writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
- if (dimensionKey.hasDimensionKeyInCondition()) {
- writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInCondition(),
- FIELD_ID_DIMENSION_LEAF_IN_CONDITION,
- str_set, protoOutput);
- }
+ }
+ // Then fill slice_by_state.
+ for (auto state : dimensionKey.getStateValuesKey().getValues()) {
+ uint64_t stateToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+ FIELD_ID_SLICE_BY_STATE);
+ writeStateToProto(state, protoOutput);
+ protoOutput->end(stateToken);
}
// Then fill bucket_info (DurationBucketInfo).
for (const auto& bucket : pair.second) {
@@ -614,19 +454,11 @@ void DurationMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs
const int64_t& nextBucketStartTimeNs) {
for (auto whatIt = mCurrentSlicedDurationTrackerMap.begin();
whatIt != mCurrentSlicedDurationTrackerMap.end();) {
- for (auto it = whatIt->second.begin(); it != whatIt->second.end();) {
- if (it->second->flushCurrentBucket(eventTimeNs, &mPastBuckets)) {
- VLOG("erase bucket for key %s %s", whatIt->first.toString().c_str(),
- it->first.toString().c_str());
- it = whatIt->second.erase(it);
- } else {
- ++it;
- }
- }
- if (whatIt->second.empty()) {
+ if (whatIt->second->flushCurrentBucket(eventTimeNs, &mPastBuckets)) {
+ VLOG("erase bucket for key %s", whatIt->first.toString().c_str());
whatIt = mCurrentSlicedDurationTrackerMap.erase(whatIt);
} else {
- whatIt++;
+ ++whatIt;
}
}
StatsdStats::getInstance().noteBucketCount(mMetricId);
@@ -642,34 +474,15 @@ void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
(unsigned long)mCurrentSlicedDurationTrackerMap.size());
if (verbose) {
for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- for (const auto& slice : whatIt.second) {
- fprintf(out, "\t(what)%s\t(condition)%s\n", whatIt.first.toString().c_str(),
- slice.first.toString().c_str());
- slice.second->dumpStates(out, verbose);
- }
+ fprintf(out, "\t(what)%s\n", whatIt.first.toString().c_str());
+ whatIt.second->dumpStates(out, verbose);
}
}
}
bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
auto whatIt = mCurrentSlicedDurationTrackerMap.find(newKey.getDimensionKeyInWhat());
- if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
- auto condIt = whatIt->second.find(newKey.getDimensionKeyInCondition());
- if (condIt != whatIt->second.end()) {
- return false;
- }
- if (whatIt->second.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
- size_t newTupleCount = whatIt->second.size() + 1;
- StatsdStats::getInstance().noteMetricDimensionInConditionSize(
- mConfigKey, mMetricId, newTupleCount);
- // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
- if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
- ALOGE("DurationMetric %lld dropping data for condition dimension key %s",
- (long long)mMetricId, newKey.getDimensionKeyInCondition().toString().c_str());
- return true;
- }
- }
- } else {
+ if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
// 1. Report the tuple count if the tuple count > soft limit
if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1;
@@ -679,6 +492,7 @@ bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
ALOGE("DurationMetric %lld dropping data for what dimension key %s",
(long long)mMetricId, newKey.getDimensionKeyInWhat().toString().c_str());
+ StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId);
return true;
}
}
@@ -690,46 +504,36 @@ void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey
const ConditionKey& conditionKeys,
bool condition, const LogEvent& event) {
const auto& whatKey = eventKey.getDimensionKeyInWhat();
- const auto& condKey = eventKey.getDimensionKeyInCondition();
-
auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey);
if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
if (hitGuardRailLocked(eventKey)) {
return;
}
- mCurrentSlicedDurationTrackerMap[whatKey][condKey] = createDurationTracker(eventKey);
- } else {
- if (whatIt->second.find(condKey) == whatIt->second.end()) {
- if (hitGuardRailLocked(eventKey)) {
- return;
- }
- mCurrentSlicedDurationTrackerMap[whatKey][condKey] = createDurationTracker(eventKey);
- }
+ mCurrentSlicedDurationTrackerMap[whatKey] = createDurationTracker(eventKey);
}
- auto it = mCurrentSlicedDurationTrackerMap.find(whatKey)->second.find(condKey);
+ auto it = mCurrentSlicedDurationTrackerMap.find(whatKey);
if (mUseWhatDimensionAsInternalDimension) {
- it->second->noteStart(whatKey, condition,
- event.GetElapsedTimestampNs(), conditionKeys);
+ it->second->noteStart(whatKey, condition, event.GetElapsedTimestampNs(), conditionKeys);
return;
}
if (mInternalDimensions.empty()) {
- it->second->noteStart(DEFAULT_DIMENSION_KEY, condition,
- event.GetElapsedTimestampNs(), conditionKeys);
+ it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, event.GetElapsedTimestampNs(),
+ conditionKeys);
} else {
HashableDimensionKey dimensionKey = DEFAULT_DIMENSION_KEY;
filterValues(mInternalDimensions, event.getValues(), &dimensionKey);
- it->second->noteStart(
- dimensionKey, condition, event.GetElapsedTimestampNs(), conditionKeys);
+ it->second->noteStart(dimensionKey, condition, event.GetElapsedTimestampNs(),
+ conditionKeys);
}
}
void DurationMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKeys, bool condition,
- const LogEvent& event) {
+ const ConditionKey& conditionKeys, bool condition, const LogEvent& event,
+ const map<int, HashableDimensionKey>& statePrimaryKeys) {
ALOGW("Not used in duration tracker.");
}
@@ -747,18 +551,49 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
// Handles Stopall events.
if (matcherIndex == mStopAllIndex) {
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- for (auto& pair : whatIt.second) {
- pair.second->noteStopAll(event.GetElapsedTimestampNs());
- }
+ whatIt.second->noteStopAll(event.GetElapsedTimestampNs());
}
return;
}
- HashableDimensionKey dimensionInWhat;
+ HashableDimensionKey dimensionInWhat = DEFAULT_DIMENSION_KEY;
if (!mDimensionsInWhat.empty()) {
filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
- } else {
- dimensionInWhat = DEFAULT_DIMENSION_KEY;
+ }
+
+ // Stores atom id to primary key pairs for each state atom that the metric is
+ // sliced by.
+ std::map<int, HashableDimensionKey> statePrimaryKeys;
+
+ // For states with primary fields, use MetricStateLinks to get the primary
+ // field values from the log event. These values will form a primary key
+ // that will be used to query StateTracker for the correct state value.
+ for (const auto& stateLink : mMetric2StateLinks) {
+ getDimensionForState(event.getValues(), stateLink,
+ &statePrimaryKeys[stateLink.stateAtomId]);
+ }
+
+ // For each sliced state, query StateTracker for the state value using
+ // either the primary key from the previous step or the DEFAULT_DIMENSION_KEY.
+ //
+ // Expected functionality: for any case where the MetricStateLinks are
+ // initialized incorrectly (ex. # of state links != # of primary fields, no
+ // links are provided for a state with primary fields, links are provided
+ // in the wrong order, etc.), StateTracker will simply return kStateUnknown
+ // when queried using an incorrect key.
+ HashableDimensionKey stateValuesKey = DEFAULT_DIMENSION_KEY;
+ for (auto atomId : mSlicedStateAtoms) {
+ FieldValue value;
+ if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) {
+ // found a primary key for this state, query using the key
+ queryStateValue(atomId, statePrimaryKeys[atomId], &value);
+ } else {
+ // if no MetricStateLinks exist for this state atom,
+ // query using the default dimension key (empty HashableDimensionKey)
+ queryStateValue(atomId, DEFAULT_DIMENSION_KEY, &value);
+ }
+ mapStateValue(atomId, &value);
+ stateValuesKey.addValue(value);
}
// Handles Stop events.
@@ -766,9 +601,7 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
if (mUseWhatDimensionAsInternalDimension) {
auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
- for (const auto& condIt : whatIt->second) {
- condIt.second->noteStop(dimensionInWhat, event.GetElapsedTimestampNs(), false);
- }
+ whatIt->second->noteStop(dimensionInWhat, event.GetElapsedTimestampNs(), false);
}
return;
}
@@ -780,62 +613,31 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
- for (const auto& condIt : whatIt->second) {
- condIt.second->noteStop(
- internalDimensionKey, event.GetElapsedTimestampNs(), false);
- }
+ whatIt->second->noteStop(internalDimensionKey, event.GetElapsedTimestampNs(), false);
}
return;
}
bool condition;
ConditionKey conditionKey;
- std::unordered_set<HashableDimensionKey> dimensionKeysInCondition;
if (mConditionSliced) {
for (const auto& link : mMetric2ConditionLinks) {
getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
}
auto conditionState =
- mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
- !mSameConditionDimensionsInTracker,
- !mHasLinksToAllConditionDimensionsInTracker,
- &dimensionKeysInCondition);
+ mWizard->query(mConditionTrackerIndex, conditionKey,
+ !mHasLinksToAllConditionDimensionsInTracker);
condition = conditionState == ConditionState::kTrue;
- if (mDimensionsInCondition.empty() && condition) {
- dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
- }
} else {
// TODO: The unknown condition state is not handled here, we should fix it.
condition = mCondition == ConditionState::kTrue;
- if (condition) {
- dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
- }
}
condition = condition && mIsActive;
- if (dimensionKeysInCondition.empty()) {
- handleStartEvent(MetricDimensionKey(dimensionInWhat, DEFAULT_DIMENSION_KEY),
- conditionKey, condition, event);
- } else {
- auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
- // If the what dimension is already there, we should update all the trackers even
- // the condition is false.
- if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
- for (const auto& condIt : whatIt->second) {
- const bool cond = dimensionKeysInCondition.find(condIt.first) !=
- dimensionKeysInCondition.end() && condition;
- handleStartEvent(MetricDimensionKey(dimensionInWhat, condIt.first),
- conditionKey, cond, event);
- dimensionKeysInCondition.erase(condIt.first);
- }
- }
- for (const auto& conditionDimension : dimensionKeysInCondition) {
- handleStartEvent(MetricDimensionKey(dimensionInWhat, conditionDimension), conditionKey,
- condition, event);
- }
- }
+ handleStartEvent(MetricDimensionKey(dimensionInWhat, stateValuesKey), conditionKey, condition,
+ event);
}
size_t DurationMetricProducer::byteSizeLocked() const {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 56c9fd68eac5..bfe1010c89de 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -38,24 +38,33 @@ namespace statsd {
class DurationMetricProducer : public MetricProducer {
public:
- DurationMetricProducer(const ConfigKey& key, const DurationMetric& durationMetric,
- const int conditionIndex, const size_t startIndex,
- const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
- const sp<ConditionWizard>& wizard,
- const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs);
+ DurationMetricProducer(
+ const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex,
+ const vector<ConditionState>& initialConditionCache, const size_t startIndex,
+ const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
+ const sp<ConditionWizard>& wizard, const FieldMatcher& internalDimensions,
+ const int64_t timeBaseNs, const int64_t startTimeNs,
+ const unordered_map<int, shared_ptr<Activation>>& eventActivationMap = {},
+ const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap = {},
+ const vector<int>& slicedStateAtoms = {},
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
virtual ~DurationMetricProducer();
sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
const sp<AlarmMonitor>& anomalyAlarmMonitor) override;
+ void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
+ const HashableDimensionKey& primaryKey, const FieldValue& oldState,
+ const FieldValue& newState) override;
+
protected:
void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) override;
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKeys, bool condition,
- const LogEvent& event) override;
+ const ConditionKey& conditionKeys, bool condition, const LogEvent& event,
+ const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
private:
void handleStartEvent(const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys,
@@ -127,13 +136,12 @@ private:
std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>> mPastBuckets;
// The duration trackers in the current bucket.
- std::unordered_map<HashableDimensionKey,
- std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>>
+ std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>
mCurrentSlicedDurationTrackerMap;
// Helper function to create a duration tracker given the metric aggregation type.
std::unique_ptr<DurationTracker> createDurationTracker(
- const MetricDimensionKey& eventKey) const;
+ const MetricDimensionKey& eventKey) const;
// This hides the base class's std::vector<sp<AnomalyTracker>> mAnomalyTrackers
std::vector<sp<DurationAnomalyTracker>> mAnomalyTrackers;
@@ -146,12 +154,14 @@ private:
FRIEND_TEST(DurationMetricTrackerTest, TestNoCondition);
FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedCondition);
FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState);
- FRIEND_TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade);
- FRIEND_TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket);
- FRIEND_TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade);
- FRIEND_TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket);
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicates);
FRIEND_TEST(DurationMetricTrackerTest, TestFirstBucket);
+
+ FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestSumDuration);
+ FRIEND_TEST(DurationMetricProducerTest_PartialBucket,
+ TestSumDurationWithSplitInFollowingBucket);
+ FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDuration);
+ FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextBucket);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 96133bd0a38d..dc0036a687f3 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -36,6 +36,7 @@ using std::map;
using std::string;
using std::unordered_map;
using std::vector;
+using std::shared_ptr;
namespace android {
namespace os {
@@ -51,11 +52,16 @@ const int FIELD_ID_DATA = 1;
const int FIELD_ID_ELAPSED_TIMESTAMP_NANOS = 1;
const int FIELD_ID_ATOMS = 2;
-EventMetricProducer::EventMetricProducer(const ConfigKey& key, const EventMetric& metric,
- const int conditionIndex,
- const sp<ConditionWizard>& wizard,
- const int64_t startTimeNs)
- : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard) {
+EventMetricProducer::EventMetricProducer(
+ const ConfigKey& key, const EventMetric& metric, const int conditionIndex,
+ const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+ const int64_t startTimeNs,
+ const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+ const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+ const vector<int>& slicedStateAtoms,
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
+ : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, initialConditionCache, wizard,
+ eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap) {
if (metric.links().size() > 0) {
for (const auto& link : metric.links()) {
Metric2Condition mc;
@@ -138,16 +144,15 @@ void EventMetricProducer::onConditionChangedLocked(const bool conditionMet,
void EventMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition,
- const LogEvent& event) {
+ const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+ const map<int, HashableDimensionKey>& statePrimaryKeys) {
if (!condition) {
return;
}
uint64_t wrapperToken =
mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
- const int64_t elapsedTimeNs = truncateTimestampIfNecessary(
- event.GetTagId(), event.GetElapsedTimestampNs());
+ const int64_t elapsedTimeNs = truncateTimestampIfNecessary(event);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_ELAPSED_TIMESTAMP_NANOS, (long long) elapsedTimeNs);
uint64_t eventToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOMS);
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 74e6bc845c04..bfb2de36fad4 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -33,17 +33,23 @@ namespace statsd {
class EventMetricProducer : public MetricProducer {
public:
- EventMetricProducer(const ConfigKey& key, const EventMetric& eventMetric,
- const int conditionIndex, const sp<ConditionWizard>& wizard,
- const int64_t startTimeNs);
+ EventMetricProducer(
+ const ConfigKey& key, const EventMetric& eventMetric, const int conditionIndex,
+ const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+ const int64_t startTimeNs,
+ const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
+ const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+ eventDeactivationMap = {},
+ const vector<int>& slicedStateAtoms = {},
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
virtual ~EventMetricProducer();
private:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition,
- const LogEvent& event) override;
+ const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+ const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
void onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 4f437d1af51a..020f4b638f4d 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -46,19 +46,21 @@ const int FIELD_ID_GAUGE_METRICS = 8;
const int FIELD_ID_TIME_BASE = 9;
const int FIELD_ID_BUCKET_SIZE = 10;
const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
-const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
const int FIELD_ID_IS_ACTIVE = 14;
// for GaugeMetricDataWrapper
const int FIELD_ID_DATA = 1;
const int FIELD_ID_SKIPPED = 2;
+// for SkippedBuckets
const int FIELD_ID_SKIPPED_START_MILLIS = 3;
const int FIELD_ID_SKIPPED_END_MILLIS = 4;
+const int FIELD_ID_SKIPPED_DROP_EVENT = 5;
+// for DumpEvent Proto
+const int FIELD_ID_BUCKET_DROP_REASON = 1;
+const int FIELD_ID_DROP_TIME = 2;
// for GaugeMetricData
const int FIELD_ID_DIMENSION_IN_WHAT = 1;
-const int FIELD_ID_DIMENSION_IN_CONDITION = 2;
const int FIELD_ID_BUCKET_INFO = 3;
const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
-const int FIELD_ID_DIMENSION_LEAF_IN_CONDITION = 5;
// for GaugeBucketInfo
const int FIELD_ID_ATOM = 3;
const int FIELD_ID_ELAPSED_ATOM_TIMESTAMP = 4;
@@ -68,11 +70,15 @@ const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 8;
GaugeMetricProducer::GaugeMetricProducer(
const ConfigKey& key, const GaugeMetric& metric, const int conditionIndex,
- const sp<ConditionWizard>& wizard, const int whatMatcherIndex,
- const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int triggerAtomId,
- const int atomId, const int64_t timeBaseNs, const int64_t startTimeNs,
- const sp<StatsPullerManager>& pullerManager)
- : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
+ const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+ const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard,
+ const int pullTagId, const int triggerAtomId, const int atomId, const int64_t timeBaseNs,
+ const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+ const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap)
+ : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard,
+ eventActivationMap, eventDeactivationMap, /*slicedStateAtoms=*/{},
+ /*stateGroupMap=*/{}),
mWhatMatcherIndex(whatMatcherIndex),
mEventMatcherWizard(matcherWizard),
mPullerManager(pullerManager),
@@ -113,10 +119,6 @@ GaugeMetricProducer::GaugeMetricProducer(
mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
}
- if (metric.has_dimensions_in_condition()) {
- translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition);
- }
-
if (metric.links().size() > 0) {
for (const auto& link : metric.links()) {
Metric2Condition mc;
@@ -125,19 +127,18 @@ GaugeMetricProducer::GaugeMetricProducer(
translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
mMetric2ConditionLinks.push_back(mc);
}
+ mConditionSliced = true;
}
- mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
- mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()) ||
- HasPositionALL(metric.dimensions_in_condition());
+ mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what());
flushIfNeededLocked(startTimeNs);
// Kicks off the puller immediately.
if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
- mPullerManager->RegisterReceiver(mPullTagId, this, getCurrentBucketEndTimeNs(),
+ mPullerManager->RegisterReceiver(mPullTagId, mConfigKey, this, getCurrentBucketEndTimeNs(),
mBucketSizeNs);
}
- // Adjust start for partial bucket
+ // Adjust start for partial first bucket and then pull if needed
mCurrentBucketStartTimeNs = startTimeNs;
VLOG("Gauge metric %lld created. bucket size %lld start_time: %lld sliced %d",
@@ -148,7 +149,7 @@ GaugeMetricProducer::GaugeMetricProducer(
GaugeMetricProducer::~GaugeMetricProducer() {
VLOG("~GaugeMetricProducer() called");
if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
- mPullerManager->UnRegisterReceiver(mPullTagId, this);
+ mPullerManager->UnRegisterReceiver(mPullTagId, mConfigKey, this);
}
}
@@ -162,10 +163,9 @@ void GaugeMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
(unsigned long)mCurrentSlicedBucket->size());
if (verbose) {
for (const auto& it : *mCurrentSlicedBucket) {
- fprintf(out, "\t(what)%s\t(condition)%s %d atoms\n",
- it.first.getDimensionKeyInWhat().toString().c_str(),
- it.first.getDimensionKeyInCondition().toString().c_str(),
- (int)it.second.size());
+ fprintf(out, "\t(what)%s\t(states)%s %d atoms\n",
+ it.first.getDimensionKeyInWhat().toString().c_str(),
+ it.first.getStateValuesKey().toString().c_str(), (int)it.second.size());
}
}
}
@@ -192,7 +192,7 @@ void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
- if (mPastBuckets.empty()) {
+ if (mPastBuckets.empty() && mSkippedBuckets.empty()) {
return;
}
@@ -207,23 +207,25 @@ void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
protoOutput->end(dimenPathToken);
}
- if (!mDimensionsInCondition.empty()) {
- uint64_t dimenPathToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_CONDITION);
- writeDimensionPathToProto(mDimensionsInCondition, protoOutput);
- protoOutput->end(dimenPathToken);
- }
}
uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS);
- for (const auto& pair : mSkippedBuckets) {
+ for (const auto& skippedBucket : mSkippedBuckets) {
uint64_t wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SKIPPED);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_START_MILLIS,
- (long long)(NanoToMillis(pair.first)));
+ (long long)(NanoToMillis(skippedBucket.bucketStartTimeNs)));
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_END_MILLIS,
- (long long)(NanoToMillis(pair.second)));
+ (long long)(NanoToMillis(skippedBucket.bucketEndTimeNs)));
+
+ for (const auto& dropEvent : skippedBucket.dropEvents) {
+ uint64_t dropEventToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+ FIELD_ID_SKIPPED_DROP_EVENT);
+ protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_BUCKET_DROP_REASON, dropEvent.reason);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DROP_TIME, (long long) (NanoToMillis(dropEvent.dropTimeNs)));
+ protoOutput->end(dropEventToken);
+ }
protoOutput->end(wrapperToken);
}
@@ -240,22 +242,9 @@ void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
protoOutput->end(dimensionToken);
-
- if (dimensionKey.hasDimensionKeyInCondition()) {
- uint64_t dimensionInConditionToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
- writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(),
- str_set, protoOutput);
- protoOutput->end(dimensionInConditionToken);
- }
} else {
writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
- if (dimensionKey.hasDimensionKeyInCondition()) {
- writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInCondition(),
- FIELD_ID_DIMENSION_LEAF_IN_CONDITION,
- str_set, protoOutput);
- }
}
// Then fill bucket_info (GaugeBucketInfo).
@@ -282,11 +271,9 @@ void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
protoOutput->end(atomsToken);
}
for (const auto& atom : bucket.mGaugeAtoms) {
- const int64_t elapsedTimestampNs =
- truncateTimestampIfNecessary(mAtomId, atom.mElapsedTimestamps);
- protoOutput->write(
- FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_ELAPSED_ATOM_TIMESTAMP,
- (long long)elapsedTimestampNs);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED |
+ FIELD_ID_ELAPSED_ATOM_TIMESTAMP,
+ (long long)atom.mElapsedTimestampNs);
}
}
protoOutput->end(bucketInfoToken);
@@ -335,18 +322,17 @@ void GaugeMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
return;
}
vector<std::shared_ptr<LogEvent>> allData;
- if (!mPullerManager->Pull(mPullTagId, &allData)) {
+ if (!mPullerManager->Pull(mPullTagId, mConfigKey, timestampNs, &allData)) {
ALOGE("Gauge Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
return;
}
const int64_t pullDelayNs = getElapsedRealtimeNs() - timestampNs;
+ StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
if (pullDelayNs > mMaxPullDelayNs) {
ALOGE("Pull finish too late for atom %d", mPullTagId);
StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId);
- StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
return;
}
- StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
for (const auto& data : allData) {
LogEvent localCopy = data->makeCopy();
localCopy.setElapsedTimestampNs(timestampNs);
@@ -429,6 +415,13 @@ void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven
if (!pullSuccess || allData.size() == 0) {
return;
}
+ const int64_t pullDelayNs = getElapsedRealtimeNs() - originalPullTimeNs;
+ StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
+ if (pullDelayNs > mMaxPullDelayNs) {
+ ALOGE("Pull finish too late for atom %d", mPullTagId);
+ StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId);
+ return;
+ }
for (const auto& data : allData) {
if (mEventMatcherWizard->matchLogEvent(
*data, mWhatMatcherIndex) == MatchingState::kMatched) {
@@ -449,6 +442,7 @@ bool GaugeMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
if (newTupleCount > mDimensionHardLimit) {
ALOGE("GaugeMetric %lld dropping data for dimension key %s",
(long long)mMetricId, newKey.toString().c_str());
+ StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId);
return true;
}
}
@@ -458,8 +452,8 @@ bool GaugeMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
void GaugeMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition,
- const LogEvent& event) {
+ const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+ const map<int, HashableDimensionKey>& statePrimaryKeys) {
if (condition == false) {
return;
}
@@ -488,7 +482,9 @@ void GaugeMetricProducer::onMatchedLogEventInternalLocked(
if ((*mCurrentSlicedBucket)[eventKey].size() >= mGaugeAtomsPerDimensionLimit) {
return;
}
- GaugeAtom gaugeAtom(getGaugeFields(event), eventTimeNs);
+
+ const int64_t truncatedElapsedTimestampNs = truncateTimestampIfNecessary(event);
+ GaugeAtom gaugeAtom(getGaugeFields(event), truncatedElapsedTimestampNs);
(*mCurrentSlicedBucket)[eventKey].push_back(gaugeAtom);
// Anomaly detection on gauge metric only works when there is one numeric
// field specified.
@@ -558,16 +554,16 @@ void GaugeMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
void GaugeMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
const int64_t& nextBucketStartTimeNs) {
int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
+ int64_t bucketEndTime = eventTimeNs < fullBucketEndTimeNs ? eventTimeNs : fullBucketEndTimeNs;
GaugeBucket info;
info.mBucketStartNs = mCurrentBucketStartTimeNs;
- if (eventTimeNs < fullBucketEndTimeNs) {
- info.mBucketEndNs = eventTimeNs;
- } else {
- info.mBucketEndNs = fullBucketEndTimeNs;
- }
+ info.mBucketEndNs = bucketEndTime;
- if (info.mBucketEndNs - mCurrentBucketStartTimeNs >= mMinBucketSizeNs) {
+ // Add bucket to mPastBuckets if bucket is large enough.
+ // Otherwise, drop the bucket data and add bucket metadata to mSkippedBuckets.
+ bool isBucketLargeEnough = info.mBucketEndNs - mCurrentBucketStartTimeNs >= mMinBucketSizeNs;
+ if (isBucketLargeEnough) {
for (const auto& slice : *mCurrentSlicedBucket) {
info.mGaugeAtoms = slice.second;
auto& bucketList = mPastBuckets[slice.first];
@@ -576,7 +572,13 @@ void GaugeMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
slice.first.toString().c_str());
}
} else {
- mSkippedBuckets.emplace_back(info.mBucketStartNs, info.mBucketEndNs);
+ mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs;
+ mCurrentSkippedBucket.bucketEndTimeNs = bucketEndTime;
+ if (!maxDropEventsReached()) {
+ mCurrentSkippedBucket.dropEvents.emplace_back(
+ buildDropEvent(eventTimeNs, BucketDropReason::BUCKET_TOO_SMALL));
+ }
+ mSkippedBuckets.emplace_back(mCurrentSkippedBucket);
}
// If we have anomaly trackers, we need to update the partial bucket values.
@@ -595,6 +597,7 @@ void GaugeMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
StatsdStats::getInstance().noteBucketCount(mMetricId);
mCurrentSlicedBucket = std::make_shared<DimToGaugeAtomsMap>();
mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
+ mCurrentSkippedBucket.reset();
}
size_t GaugeMetricProducer::byteSizeLocked() const {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index a612adf8a38b..2fc772b6b641 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -35,10 +35,10 @@ namespace statsd {
struct GaugeAtom {
GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t elapsedTimeNs)
- : mFields(fields), mElapsedTimestamps(elapsedTimeNs) {
+ : mFields(fields), mElapsedTimestampNs(elapsedTimeNs) {
}
std::shared_ptr<vector<FieldValue>> mFields;
- int64_t mElapsedTimestamps;
+ int64_t mElapsedTimestampNs;
};
struct GaugeBucket {
@@ -56,13 +56,16 @@ typedef std::unordered_map<MetricDimensionKey, std::vector<GaugeAtom>>
// producer always reports the guage at the earliest time of the bucket when the condition is met.
class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
public:
- GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
- const int conditionIndex, const sp<ConditionWizard>& conditionWizard,
- const int whatMatcherIndex,
- const sp<EventMatcherWizard>& matcherWizard,
- const int pullTagId, const int triggerAtomId, const int atomId,
- const int64_t timeBaseNs, const int64_t startTimeNs,
- const sp<StatsPullerManager>& pullerManager);
+ GaugeMetricProducer(
+ const ConfigKey& key, const GaugeMetric& gaugeMetric, const int conditionIndex,
+ const vector<ConditionState>& initialConditionCache,
+ const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
+ const sp<EventMatcherWizard>& matcherWizard, const int pullTagId,
+ const int triggerAtomId, const int atomId, const int64_t timeBaseNs,
+ const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
+ const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+ eventDeactivationMap = {});
virtual ~GaugeMetricProducer();
@@ -71,18 +74,23 @@ public:
bool pullSuccess, int64_t originalPullTimeNs) override;
// GaugeMetric needs to immediately trigger another pull when we create the partial bucket.
- void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
- const int64_t version) override {
+ void notifyAppUpgrade(const int64_t& eventTimeNs) override {
std::lock_guard<std::mutex> lock(mMutex);
if (!mSplitBucketForAppUpgrade) {
return;
}
- if (eventTimeNs > getCurrentBucketEndTimeNs()) {
- // Flush full buckets on the normal path up to the latest bucket boundary.
- flushIfNeededLocked(eventTimeNs);
+ flushLocked(eventTimeNs);
+ if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
+ pullAndMatchEventsLocked(eventTimeNs);
}
- flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
+ };
+
+ // GaugeMetric needs to immediately trigger another pull when we create the partial bucket.
+ void onStatsdInitCompleted(const int64_t& eventTimeNs) override {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ flushLocked(eventTimeNs);
if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
pullAndMatchEventsLocked(eventTimeNs);
}
@@ -91,8 +99,8 @@ public:
protected:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition,
- const LogEvent& event) override;
+ const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+ const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
private:
void onDumpReportLocked(const int64_t dumpTimeNs,
@@ -156,9 +164,6 @@ private:
// this slice (ie, for partial buckets, we use the last partial bucket in this full bucket).
std::shared_ptr<DimToValMap> mCurrentSlicedBucketForAnomaly;
- // Pairs of (elapsed start, elapsed end) denoting buckets that were skipped.
- std::list<std::pair<int64_t, int64_t>> mSkippedBuckets;
-
const int64_t mMinBucketSizeNs;
// Translate Atom based bucket to single numeric value bucket for anomaly and updates the map
@@ -191,13 +196,14 @@ private:
FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition);
- FRIEND_TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade);
- FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithUpgrade);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection);
FRIEND_TEST(GaugeMetricProducerTest, TestFirstBucket);
FRIEND_TEST(GaugeMetricProducerTest, TestPullOnTrigger);
FRIEND_TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput);
+
+ FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPushedEvents);
+ FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPulled);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 36434eb7ceb4..fe143e496373 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -16,8 +16,12 @@
#define DEBUG false // STOPSHIP if true
#include "Log.h"
+
#include "MetricProducer.h"
+#include "../guardrail/StatsdStats.h"
+#include "state/StateTracker.h"
+
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_ENUM;
using android::util::FIELD_TYPE_INT32;
@@ -39,6 +43,34 @@ const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX = 1;
const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS = 2;
const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE = 3;
+MetricProducer::MetricProducer(
+ const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
+ const int conditionIndex, const vector<ConditionState>& initialConditionCache,
+ const sp<ConditionWizard>& wizard,
+ const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
+ const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+ eventDeactivationMap,
+ const vector<int>& slicedStateAtoms,
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
+ : mMetricId(metricId),
+ mConfigKey(key),
+ mTimeBaseNs(timeBaseNs),
+ mCurrentBucketStartTimeNs(timeBaseNs),
+ mCurrentBucketNum(0),
+ mCondition(initialCondition(conditionIndex, initialConditionCache)),
+ mConditionTrackerIndex(conditionIndex),
+ mConditionSliced(false),
+ mWizard(wizard),
+ mContainANYPositionInDimensionsInWhat(false),
+ mSliceByPositionALL(false),
+ mHasLinksToAllConditionDimensionsInTracker(false),
+ mEventActivationMap(eventActivationMap),
+ mEventDeactivationMap(eventDeactivationMap),
+ mIsActive(mEventActivationMap.empty()),
+ mSlicedStateAtoms(slicedStateAtoms),
+ mStateGroupMap(stateGroupMap) {
+}
+
void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
if (!mIsActive) {
return;
@@ -51,38 +83,59 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo
bool condition;
ConditionKey conditionKey;
- std::unordered_set<HashableDimensionKey> dimensionKeysInCondition;
if (mConditionSliced) {
for (const auto& link : mMetric2ConditionLinks) {
getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
}
auto conditionState =
- mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
- !mSameConditionDimensionsInTracker,
- !mHasLinksToAllConditionDimensionsInTracker,
- &dimensionKeysInCondition);
+ mWizard->query(mConditionTrackerIndex, conditionKey,
+ !mHasLinksToAllConditionDimensionsInTracker);
condition = (conditionState == ConditionState::kTrue);
} else {
// TODO: The unknown condition state is not handled here, we should fix it.
condition = mCondition == ConditionState::kTrue;
}
- if (mDimensionsInCondition.empty() && condition) {
- dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
+ // Stores atom id to primary key pairs for each state atom that the metric is
+ // sliced by.
+ std::map<int32_t, HashableDimensionKey> statePrimaryKeys;
+
+ // For states with primary fields, use MetricStateLinks to get the primary
+ // field values from the log event. These values will form a primary key
+ // that will be used to query StateTracker for the correct state value.
+ for (const auto& stateLink : mMetric2StateLinks) {
+ getDimensionForState(event.getValues(), stateLink,
+ &statePrimaryKeys[stateLink.stateAtomId]);
+ }
+
+ // For each sliced state, query StateTracker for the state value using
+ // either the primary key from the previous step or the DEFAULT_DIMENSION_KEY.
+ //
+ // Expected functionality: for any case where the MetricStateLinks are
+ // initialized incorrectly (ex. # of state links != # of primary fields, no
+ // links are provided for a state with primary fields, links are provided
+ // in the wrong order, etc.), StateTracker will simply return kStateUnknown
+ // when queried using an incorrect key.
+ HashableDimensionKey stateValuesKey;
+ for (auto atomId : mSlicedStateAtoms) {
+ FieldValue value;
+ if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) {
+ // found a primary key for this state, query using the key
+ queryStateValue(atomId, statePrimaryKeys[atomId], &value);
+ } else {
+ // if no MetricStateLinks exist for this state atom,
+ // query using the default dimension key (empty HashableDimensionKey)
+ queryStateValue(atomId, DEFAULT_DIMENSION_KEY, &value);
+ }
+ mapStateValue(atomId, &value);
+ stateValuesKey.addValue(value);
}
HashableDimensionKey dimensionInWhat;
filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
- MetricDimensionKey metricKey(dimensionInWhat, DEFAULT_DIMENSION_KEY);
- for (const auto& conditionDimensionKey : dimensionKeysInCondition) {
- metricKey.setDimensionKeyInCondition(conditionDimensionKey);
- onMatchedLogEventInternalLocked(
- matcherIndex, metricKey, conditionKey, condition, event);
- }
- if (dimensionKeysInCondition.empty()) {
- onMatchedLogEventInternalLocked(
- matcherIndex, metricKey, conditionKey, condition, event);
- }
+ MetricDimensionKey metricKey(dimensionInWhat, stateValuesKey);
+ onMatchedLogEventInternalLocked(matcherIndex, metricKey, conditionKey, condition, event,
+ statePrimaryKeys);
}
bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) {
@@ -110,24 +163,6 @@ void MetricProducer::flushIfExpire(int64_t elapsedTimestampNs) {
}
}
-void MetricProducer::addActivation(int activationTrackerIndex, const ActivationType& activationType,
- int64_t ttl_seconds, int deactivationTrackerIndex) {
- std::lock_guard<std::mutex> lock(mMutex);
- // When a metric producer does not depend on any activation, its mIsActive is true.
- // Therefore, if this is the 1st activation, mIsActive will turn to false. Otherwise it does not
- // change.
- if (mEventActivationMap.empty()) {
- mIsActive = false;
- }
- std::shared_ptr<Activation> activation =
- std::make_shared<Activation>(activationType, ttl_seconds * NS_PER_SEC);
- mEventActivationMap.emplace(activationTrackerIndex, activation);
- if (-1 != deactivationTrackerIndex) {
- auto& deactivationList = mEventDeactivationMap[deactivationTrackerIndex];
- deactivationList.push_back(activation);
- }
-}
-
void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs) {
auto it = mEventActivationMap.find(activationTrackerIndex);
if (it == mEventActivationMap.end()) {
@@ -231,6 +266,56 @@ void MetricProducer::writeActiveMetricToProtoOutputStream(
}
}
+void MetricProducer::queryStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
+ FieldValue* value) {
+ if (!StateManager::getInstance().getStateValue(atomId, queryKey, value)) {
+ value->mValue = Value(StateTracker::kStateUnknown);
+ value->mField.setTag(atomId);
+ ALOGW("StateTracker not found for state atom %d", atomId);
+ return;
+ }
+}
+
+void MetricProducer::mapStateValue(const int32_t atomId, FieldValue* value) {
+ // check if there is a state map for this atom
+ auto atomIt = mStateGroupMap.find(atomId);
+ if (atomIt == mStateGroupMap.end()) {
+ return;
+ }
+ auto valueIt = atomIt->second.find(value->mValue.int_value);
+ if (valueIt == atomIt->second.end()) {
+ // state map exists, but value was not put in a state group
+ // so set mValue to kStateUnknown
+ // TODO(tsaichristine): handle incomplete state maps
+ value->mValue.setInt(StateTracker::kStateUnknown);
+ } else {
+ // set mValue to group_id
+ value->mValue.setLong(valueIt->second);
+ }
+}
+
+HashableDimensionKey MetricProducer::getUnknownStateKey() {
+ HashableDimensionKey stateKey;
+ for (auto atom : mSlicedStateAtoms) {
+ FieldValue fieldValue;
+ fieldValue.mField.setTag(atom);
+ fieldValue.mValue.setInt(StateTracker::kStateUnknown);
+ stateKey.addValue(fieldValue);
+ }
+ return stateKey;
+}
+
+DropEvent MetricProducer::buildDropEvent(const int64_t dropTimeNs, const BucketDropReason reason) {
+ DropEvent event;
+ event.reason = reason;
+ event.dropTimeNs = dropTimeNs;
+ return event;
+}
+
+bool MetricProducer::maxDropEventsReached() {
+ return mCurrentSkippedBucket.dropEvents.size() >= StatsdStats::kMaxLoggedBucketDropEvents;
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index c77bc0135d86..be4cd6724bb1 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -28,6 +28,8 @@
#include "config/ConfigKey.h"
#include "matchers/matcher_util.h"
#include "packages/PackageInfoListener.h"
+#include "state/StateListener.h"
+#include "state/StateManager.h"
namespace android {
namespace os {
@@ -67,61 +69,101 @@ enum DumpLatency {
NO_TIME_CONSTRAINTS = 2
};
+// Keep this in sync with BucketDropReason enum in stats_log.proto
+enum BucketDropReason {
+ // For ValueMetric, a bucket is dropped during a dump report request iff
+ // current bucket should be included, a pull is needed (pulled metric and
+ // condition is true), and we are under fast time constraints.
+ DUMP_REPORT_REQUESTED = 1,
+ EVENT_IN_WRONG_BUCKET = 2,
+ CONDITION_UNKNOWN = 3,
+ PULL_FAILED = 4,
+ PULL_DELAYED = 5,
+ DIMENSION_GUARDRAIL_REACHED = 6,
+ MULTIPLE_BUCKETS_SKIPPED = 7,
+ // Not an invalid bucket case, but the bucket is dropped.
+ BUCKET_TOO_SMALL = 8,
+ // Not an invalid bucket case, but the bucket is skipped.
+ NO_DATA = 9
+};
+
+struct Activation {
+ Activation(const ActivationType& activationType, const int64_t ttlNs)
+ : ttl_ns(ttlNs),
+ start_ns(0),
+ state(ActivationState::kNotActive),
+ activationType(activationType) {}
+
+ const int64_t ttl_ns;
+ int64_t start_ns;
+ ActivationState state;
+ const ActivationType activationType;
+};
+
+struct DropEvent {
+ // Reason for dropping the bucket and/or marking the bucket invalid.
+ BucketDropReason reason;
+ // The timestamp of the drop event.
+ int64_t dropTimeNs;
+};
+
+struct SkippedBucket {
+ // Start time of the dropped bucket.
+ int64_t bucketStartTimeNs;
+ // End time of the dropped bucket.
+ int64_t bucketEndTimeNs;
+ // List of events that invalidated this bucket.
+ std::vector<DropEvent> dropEvents;
+
+ void reset() {
+ bucketStartTimeNs = 0;
+ bucketEndTimeNs = 0;
+ dropEvents.clear();
+ }
+};
+
// A MetricProducer is responsible for compute one single metrics, creating stats log report, and
// writing the report to dropbox. MetricProducers should respond to package changes as required in
// PackageInfoListener, but if none of the metrics are slicing by package name, then the update can
// be a no-op.
-class MetricProducer : public virtual android::RefBase {
+class MetricProducer : public virtual android::RefBase, public virtual StateListener {
public:
MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
- const int conditionIndex, const sp<ConditionWizard>& wizard)
- : mMetricId(metricId),
- mConfigKey(key),
- mTimeBaseNs(timeBaseNs),
- mCurrentBucketStartTimeNs(timeBaseNs),
- mCurrentBucketNum(0),
- mCondition(initialCondition(conditionIndex)),
- mConditionSliced(false),
- mWizard(wizard),
- mConditionTrackerIndex(conditionIndex),
- mContainANYPositionInDimensionsInWhat(false),
- mSliceByPositionALL(false),
- mSameConditionDimensionsInTracker(false),
- mHasLinksToAllConditionDimensionsInTracker(false),
- mIsActive(true) {
- }
+ const int conditionIndex, const vector<ConditionState>& initialConditionCache,
+ const sp<ConditionWizard>& wizard,
+ const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
+ const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+ eventDeactivationMap,
+ const vector<int>& slicedStateAtoms,
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap);
virtual ~MetricProducer(){};
- ConditionState initialCondition(const int conditionIndex) const {
- return conditionIndex >= 0 ? ConditionState::kUnknown : ConditionState::kTrue;
+ ConditionState initialCondition(const int conditionIndex,
+ const vector<ConditionState>& initialConditionCache) const {
+ return conditionIndex >= 0 ? initialConditionCache[conditionIndex] : ConditionState::kTrue;
}
/**
- * Forces this metric to split into a partial bucket right now. If we're past a full bucket, we
- * first call the standard flushing code to flush up to the latest full bucket. Then we call
- * the flush again when the end timestamp is forced to be now, and then after flushing, update
- * the start timestamp to be now.
+ * Force a partial bucket split on app upgrade
*/
- virtual void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
- const int64_t version) {
+ virtual void notifyAppUpgrade(const int64_t& eventTimeNs) {
std::lock_guard<std::mutex> lock(mMutex);
-
- if (eventTimeNs > getCurrentBucketEndTimeNs()) {
- // Flush full buckets on the normal path up to the latest bucket boundary.
- flushIfNeededLocked(eventTimeNs);
- }
- // Now flush a partial bucket.
- flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
- // Don't update the current bucket number so that the anomaly tracker knows this bucket
- // is a partial bucket and can merge it with the previous bucket.
+ flushLocked(eventTimeNs);
};
- void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) {
+ void notifyAppRemoved(const int64_t& eventTimeNs) {
// Force buckets to split on removal also.
- notifyAppUpgrade(eventTimeNs, apk, uid, 0);
+ notifyAppUpgrade(eventTimeNs);
};
+ /**
+ * Force a partial bucket split on boot complete.
+ */
+ virtual void onStatsdInitCompleted(const int64_t& eventTimeNs) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ flushLocked(eventTimeNs);
+ }
// Consume the parsed stats log entry that already matched the "what" of the metric.
void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) {
std::lock_guard<std::mutex> lock(mMutex);
@@ -143,6 +185,10 @@ public:
return mConditionSliced;
};
+ void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
+ const HashableDimensionKey& primaryKey, const FieldValue& oldState,
+ const FieldValue& newState){};
+
// Output the metrics data to [protoOutput]. All metrics reports end with the same timestamp.
// This method clears all the past buckets.
void onDumpReport(const int64_t dumpTimeNs,
@@ -161,9 +207,9 @@ public:
return clearPastBucketsLocked(dumpTimeNs);
}
- void dumpStates(FILE* out, bool verbose) const {
+ void prepareFirstBucket() {
std::lock_guard<std::mutex> lock(mMutex);
- dumpStatesLocked(out, verbose);
+ prepareFirstBucketLocked();
}
// Returns the memory in bytes currently used to store this metric's data. Does not change
@@ -173,34 +219,9 @@ public:
return byteSizeLocked();
}
- /* If alert is valid, adds an AnomalyTracker and returns it. If invalid, returns nullptr. */
- virtual sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
- const sp<AlarmMonitor>& anomalyAlarmMonitor) {
- std::lock_guard<std::mutex> lock(mMutex);
- sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, mConfigKey);
- if (anomalyTracker != nullptr) {
- mAnomalyTrackers.push_back(anomalyTracker);
- }
- return anomalyTracker;
- }
-
- int64_t getBuckeSizeInNs() const {
- std::lock_guard<std::mutex> lock(mMutex);
- return mBucketSizeNs;
- }
-
- // Only needed for unit-testing to override guardrail.
- void setBucketSize(int64_t bucketSize) {
- mBucketSizeNs = bucketSize;
- }
-
- inline const int64_t& getMetricId() const {
- return mMetricId;
- }
-
- void loadActiveMetric(const ActiveMetric& activeMetric, int64_t currentTimeNs) {
+ void dumpStates(FILE* out, bool verbose) const {
std::lock_guard<std::mutex> lock(mMutex);
- loadActiveMetricLocked(activeMetric, currentTimeNs);
+ dumpStatesLocked(out, verbose);
}
// Let MetricProducer drop in-memory data to save memory.
@@ -212,9 +233,9 @@ public:
dropDataLocked(dropTimeNs);
}
- // For test only.
- inline int64_t getCurrentBucketNum() const {
- return mCurrentBucketNum;
+ void loadActiveMetric(const ActiveMetric& activeMetric, int64_t currentTimeNs) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ loadActiveMetricLocked(activeMetric, currentTimeNs);
}
void activate(int activationTrackerIndex, int64_t elapsedTimestampNs) {
@@ -232,59 +253,49 @@ public:
return isActiveLocked();
}
- void addActivation(int activationTrackerIndex, const ActivationType& activationType,
- int64_t ttl_seconds, int deactivationTrackerIndex = -1);
-
- void prepareFirstBucket() {
- std::lock_guard<std::mutex> lock(mMutex);
- prepareFirstBucketLocked();
- }
-
void flushIfExpire(int64_t elapsedTimestampNs);
void writeActiveMetricToProtoOutputStream(
int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
-protected:
- virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0;
- virtual void onSlicedConditionMayChangeLocked(bool overallCondition,
- const int64_t eventTime) = 0;
- virtual void onDumpReportLocked(const int64_t dumpTimeNs,
- const bool include_current_partial_bucket,
- const bool erase_data,
- const DumpLatency dumpLatency,
- std::set<string> *str_set,
- android::util::ProtoOutputStream* protoOutput) = 0;
- virtual void clearPastBucketsLocked(const int64_t dumpTimeNs) = 0;
- virtual size_t byteSizeLocked() const = 0;
- virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0;
- bool evaluateActiveStateLocked(int64_t elapsedTimestampNs);
+ // Start: getters/setters
+ inline const int64_t& getMetricId() const {
+ return mMetricId;
+ }
- void activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs);
- void cancelEventActivationLocked(int deactivationTrackerIndex);
+ // For test only.
+ inline int64_t getCurrentBucketNum() const {
+ return mCurrentBucketNum;
+ }
- inline bool isActiveLocked() const {
- return mIsActive;
+ int64_t getBucketSizeInNs() const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mBucketSizeNs;
}
- void loadActiveMetricLocked(const ActiveMetric& activeMetric, int64_t currentTimeNs);
+ inline const std::vector<int> getSlicedStateAtoms() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mSlicedStateAtoms;
+ }
- virtual void prepareFirstBucketLocked() {};
+ /* If alert is valid, adds an AnomalyTracker and returns it. If invalid, returns nullptr. */
+ virtual sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
+ const sp<AlarmMonitor>& anomalyAlarmMonitor) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, mConfigKey);
+ if (anomalyTracker != nullptr) {
+ mAnomalyTrackers.push_back(anomalyTracker);
+ }
+ return anomalyTracker;
+ }
+ // End: getters/setters
+protected:
/**
- * Flushes the current bucket if the eventTime is after the current bucket's end time. This will
- also flush the current partial bucket in memory.
+ * Flushes the current bucket if the eventTime is after the current bucket's end time.
*/
virtual void flushIfNeededLocked(const int64_t& eventTime){};
/**
- * Flushes all the data including the current partial bucket.
- */
- virtual void flushLocked(const int64_t& eventTimeNs) {
- flushIfNeededLocked(eventTimeNs);
- flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
- };
-
- /**
* For metrics that aggregate (ie, every metric producer except for EventMetricProducer),
* we need to be able to flush the current buckets on demand (ie, end the current bucket and
* start new bucket). If this function is called when eventTimeNs is greater than the current
@@ -297,12 +308,66 @@ protected:
virtual void flushCurrentBucketLocked(const int64_t& eventTimeNs,
const int64_t& nextBucketStartTimeNs) {};
+ /**
+ * Flushes all the data including the current partial bucket.
+ */
+ virtual void flushLocked(const int64_t& eventTimeNs) {
+ flushIfNeededLocked(eventTimeNs);
+ flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
+ };
+
+ /*
+ * Individual metrics can implement their own business logic here. All pre-processing is done.
+ *
+ * [matcherIndex]: the index of the matcher which matched this event. This is interesting to
+ * DurationMetric, because it has start/stop/stop_all 3 matchers.
+ * [eventKey]: the extracted dimension key for the final output. if the metric doesn't have
+ * dimensions, it will be DEFAULT_DIMENSION_KEY
+ * [conditionKey]: the keys of conditions which should be used to query the condition for this
+ * target event (from MetricConditionLink). This is passed to individual metrics
+ * because DurationMetric needs it to be cached.
+ * [condition]: whether condition is met. If condition is sliced, this is the result coming from
+ * query with ConditionWizard; If condition is not sliced, this is the
+ * nonSlicedCondition.
+ * [event]: the log event, just in case the metric needs its data, e.g., EventMetric.
+ */
+ virtual void onMatchedLogEventInternalLocked(
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
+ const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+ const map<int, HashableDimensionKey>& statePrimaryKeys) = 0;
+
+ // Consume the parsed stats log entry that already matched the "what" of the metric.
+ virtual void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event);
+ virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0;
+ virtual void onSlicedConditionMayChangeLocked(bool overallCondition,
+ const int64_t eventTime) = 0;
+ virtual void onDumpReportLocked(const int64_t dumpTimeNs,
+ const bool include_current_partial_bucket,
+ const bool erase_data,
+ const DumpLatency dumpLatency,
+ std::set<string> *str_set,
+ android::util::ProtoOutputStream* protoOutput) = 0;
+ virtual void clearPastBucketsLocked(const int64_t dumpTimeNs) = 0;
+ virtual void prepareFirstBucketLocked(){};
+ virtual size_t byteSizeLocked() const = 0;
+ virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0;
+ virtual void dropDataLocked(const int64_t dropTimeNs) = 0;
+ void loadActiveMetricLocked(const ActiveMetric& activeMetric, int64_t currentTimeNs);
+ void activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs);
+ void cancelEventActivationLocked(int deactivationTrackerIndex);
+
+ bool evaluateActiveStateLocked(int64_t elapsedTimestampNs);
+
virtual void onActiveStateChangedLocked(const int64_t& eventTimeNs) {
if (!mIsActive) {
flushLocked(eventTimeNs);
}
}
+ inline bool isActiveLocked() const {
+ return mIsActive;
+ }
+
// Convenience to compute the current bucket's end time, which is always aligned with the
// start time of the metric.
int64_t getCurrentBucketEndTimeNs() const {
@@ -313,7 +378,25 @@ protected:
return (endNs - mTimeBaseNs) / mBucketSizeNs - 1;
}
- virtual void dropDataLocked(const int64_t dropTimeNs) = 0;
+ // Query StateManager for original state value using the queryKey.
+ // The field and value are output.
+ void queryStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
+ FieldValue* value);
+
+ // If a state map exists for the given atom, replace the original state
+ // value with the group id mapped to the value.
+ // If no state map exists, keep the original state value.
+ void mapStateValue(const int32_t atomId, FieldValue* value);
+
+ // Returns a HashableDimensionKey with unknown state value for each state
+ // atom.
+ HashableDimensionKey getUnknownStateKey();
+
+ DropEvent buildDropEvent(const int64_t dropTimeNs, const BucketDropReason reason);
+
+ // Returns true if the number of drop events in the current bucket has
+ // exceeded the maximum number allowed, which is currently capped at 10.
+ bool maxDropEventsReached();
const int64_t mMetricId;
@@ -335,21 +418,17 @@ protected:
ConditionState mCondition;
+ int mConditionTrackerIndex;
+
bool mConditionSliced;
sp<ConditionWizard> mWizard;
- int mConditionTrackerIndex;
-
- vector<Matcher> mDimensionsInWhat; // The dimensions_in_what defined in statsd_config
- vector<Matcher> mDimensionsInCondition; // The dimensions_in_condition defined in statsd_config
-
bool mContainANYPositionInDimensionsInWhat;
+
bool mSliceByPositionALL;
- // True iff the condition dimensions equal to the sliced dimensions in the simple condition
- // tracker. This field is always false for combinational condition trackers.
- bool mSameConditionDimensionsInTracker;
+ vector<Matcher> mDimensionsInWhat; // The dimensions_in_what defined in statsd_config
// True iff the metric to condition links cover all dimension fields in the condition tracker.
// This field is always false for combinational condition trackers.
@@ -359,43 +438,8 @@ protected:
std::vector<sp<AnomalyTracker>> mAnomalyTrackers;
- /*
- * Individual metrics can implement their own business logic here. All pre-processing is done.
- *
- * [matcherIndex]: the index of the matcher which matched this event. This is interesting to
- * DurationMetric, because it has start/stop/stop_all 3 matchers.
- * [eventKey]: the extracted dimension key for the final output. if the metric doesn't have
- * dimensions, it will be DEFAULT_DIMENSION_KEY
- * [conditionKey]: the keys of conditions which should be used to query the condition for this
- * target event (from MetricConditionLink). This is passed to individual metrics
- * because DurationMetric needs it to be cached.
- * [condition]: whether condition is met. If condition is sliced, this is the result coming from
- * query with ConditionWizard; If condition is not sliced, this is the
- * nonSlicedCondition.
- * [event]: the log event, just in case the metric needs its data, e.g., EventMetric.
- */
- virtual void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition,
- const LogEvent& event) = 0;
-
- // Consume the parsed stats log entry that already matched the "what" of the metric.
- virtual void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event);
-
mutable std::mutex mMutex;
- struct Activation {
- Activation(const ActivationType& activationType, const int64_t ttlNs)
- : ttl_ns(ttlNs),
- start_ns(0),
- state(ActivationState::kNotActive),
- activationType(activationType) {}
-
- const int64_t ttl_ns;
- int64_t start_ns;
- ActivationState state;
- const ActivationType activationType;
- };
// When the metric producer has multiple activations, these activations are ORed to determine
// whether the metric producer is ready to generate metrics.
std::unordered_map<int, std::shared_ptr<Activation>> mEventActivationMap;
@@ -405,12 +449,37 @@ protected:
bool mIsActive;
+ // The slice_by_state atom ids defined in statsd_config.
+ const std::vector<int32_t> mSlicedStateAtoms;
+
+ // Maps atom ids and state values to group_ids (<atom_id, <value, group_id>>).
+ const std::unordered_map<int32_t, std::unordered_map<int, int64_t>> mStateGroupMap;
+
+ // MetricStateLinks defined in statsd_config that link fields in the state
+ // atom to fields in the "what" atom.
+ std::vector<Metric2State> mMetric2StateLinks;
+
+ SkippedBucket mCurrentSkippedBucket;
+ // Buckets that were invalidated and had their data dropped.
+ std::vector<SkippedBucket> mSkippedBuckets;
+
+ FRIEND_TEST(CountMetricE2eTest, TestSlicedState);
+ FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap);
+ FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates);
+ FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields);
+ FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges);
+
FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
FRIEND_TEST(DurationMetricE2eTest, TestWithCondition);
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition);
FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedState);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStateMapped);
+ FRIEND_TEST(DurationMetricE2eTest, TestSlicedStatePrimaryFieldsNotSubsetDimInWhat);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation);
@@ -424,6 +493,13 @@ protected:
FRIEND_TEST(StatsLogProcessorTest,
TestActivationOnBootMultipleActivationsDifferentActivationTypes);
FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
+
+ FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState);
+ FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions);
+ FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions);
+ FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges);
+
+ FRIEND_TEST(MetricsManagerTest, TestInitialConditions);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 1fb2b1ce5e14..60de1a24cce5 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -21,18 +21,16 @@
#include <private/android_filesystem_config.h>
#include "CountMetricProducer.h"
-#include "atoms_info.h"
#include "condition/CombinationConditionTracker.h"
#include "condition/SimpleConditionTracker.h"
#include "guardrail/StatsdStats.h"
#include "matchers/CombinationLogMatchingTracker.h"
#include "matchers/SimpleLogMatchingTracker.h"
#include "metrics_manager_util.h"
-#include "stats_util.h"
+#include "state/StateManager.h"
#include "stats_log_util.h"
-#include "statslog.h"
-
-#include <private/android_filesystem_config.h>
+#include "stats_util.h"
+#include "statslog_statsd.h"
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_INT32;
@@ -71,6 +69,9 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config,
mTtlEndNs(-1),
mLastReportTimeNs(currentTimeNs),
mLastReportWallClockNs(getWallClockNs()),
+ mPullerManager(pullerManager),
+ mWhitelistedAtomIds(config.whitelisted_atom_ids().begin(),
+ config.whitelisted_atom_ids().end()),
mShouldPersistHistory(config.persist_locally()) {
// Init the ttl end timestamp.
refreshTtl(timeBaseNs);
@@ -81,12 +82,13 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config,
mAllMetricProducers, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers,
mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap,
mActivationAtomTrackerToMetricMap, mDeactivationAtomTrackerToMetricMap,
- mMetricIndexesWithActivation, mNoReportMetricIds);
+ mAlertTrackerMap, mMetricIndexesWithActivation, mNoReportMetricIds);
mHashStringsInReport = config.hash_strings_in_metric_report();
mVersionStringsInReport = config.version_strings_in_metric_report();
mInstallerInReport = config.installer_in_metric_report();
+ // Init allowed pushed atom uids.
if (config.allowed_log_source_size() == 0) {
mConfigValid = false;
ALOGE("Log source whitelist is empty! This config won't get any data. Suggest adding at "
@@ -109,6 +111,40 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config,
}
}
+ // Init default allowed pull atom uids.
+ int numPullPackages = 0;
+ for (const string& pullSource : config.default_pull_packages()) {
+ auto it = UidMap::sAidToUidMapping.find(pullSource);
+ if (it != UidMap::sAidToUidMapping.end()) {
+ numPullPackages++;
+ mDefaultPullUids.insert(it->second);
+ } else {
+ ALOGE("Default pull atom packages must be in sAidToUidMapping");
+ mConfigValid = false;
+ }
+ }
+ // Init per-atom pull atom packages.
+ for (const PullAtomPackages& pullAtomPackages : config.pull_atom_packages()) {
+ int32_t atomId = pullAtomPackages.atom_id();
+ for (const string& pullPackage : pullAtomPackages.packages()) {
+ numPullPackages++;
+ auto it = UidMap::sAidToUidMapping.find(pullPackage);
+ if (it != UidMap::sAidToUidMapping.end()) {
+ mPullAtomUids[atomId].insert(it->second);
+ } else {
+ mPullAtomPackages[atomId].insert(pullPackage);
+ }
+ }
+ }
+ if (numPullPackages > StatsdStats::kMaxPullAtomPackages) {
+ ALOGE("Too many sources in default_pull_packages and pull_atom_packages. This is likely to "
+ "be an error in the config");
+ mConfigValid = false;
+ } else {
+ initPullAtomSources();
+ }
+ mPullerManager->RegisterPullUidProvider(mConfigKey, this);
+
// Store the sub-configs used.
for (const auto& annotation : config.annotation()) {
mAnnotations.emplace_back(annotation.field_int64(), annotation.field_int32());
@@ -149,6 +185,13 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config,
}
MetricsManager::~MetricsManager() {
+ for (auto it : mAllMetricProducers) {
+ for (int atomId : it->getSlicedStateAtoms()) {
+ StateManager::getInstance().unregisterListener(atomId, it);
+ }
+ }
+ mPullerManager->UnregisterPullUidProvider(mConfigKey, this);
+
VLOG("~MetricsManager()");
}
@@ -168,6 +211,20 @@ void MetricsManager::initLogSourceWhiteList() {
}
}
+void MetricsManager::initPullAtomSources() {
+ std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
+ mCombinedPullAtomUids.clear();
+ for (const auto& [atomId, uids] : mPullAtomUids) {
+ mCombinedPullAtomUids[atomId].insert(uids.begin(), uids.end());
+ }
+ for (const auto& [atomId, packages] : mPullAtomPackages) {
+ for (const string& pkg : packages) {
+ set<int32_t> uids = mUidMap->getAppUid(pkg);
+ mCombinedPullAtomUids[atomId].insert(uids.begin(), uids.end());
+ }
+ }
+}
+
bool MetricsManager::isConfigValid() const {
return mConfigValid;
}
@@ -175,43 +232,81 @@ bool MetricsManager::isConfigValid() const {
void MetricsManager::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
const int64_t version) {
// Inform all metric producers.
- for (auto it : mAllMetricProducers) {
- it->notifyAppUpgrade(eventTimeNs, apk, uid, version);
+ for (const auto& it : mAllMetricProducers) {
+ it->notifyAppUpgrade(eventTimeNs);
}
// check if we care this package
- if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) == mAllowedPkg.end()) {
- return;
+ if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) {
+ // We will re-initialize the whole list because we don't want to keep the multi mapping of
+ // UID<->pkg inside MetricsManager to reduce the memory usage.
+ initLogSourceWhiteList();
+ }
+
+ for (const auto& it : mPullAtomPackages) {
+ if (it.second.find(apk) != it.second.end()) {
+ initPullAtomSources();
+ return;
+ }
}
- // We will re-initialize the whole list because we don't want to keep the multi mapping of
- // UID<->pkg inside MetricsManager to reduce the memory usage.
- initLogSourceWhiteList();
}
void MetricsManager::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk,
const int uid) {
// Inform all metric producers.
- for (auto it : mAllMetricProducers) {
- it->notifyAppRemoved(eventTimeNs, apk, uid);
+ for (const auto& it : mAllMetricProducers) {
+ it->notifyAppRemoved(eventTimeNs);
}
// check if we care this package
- if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) == mAllowedPkg.end()) {
- return;
+ if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) {
+ // We will re-initialize the whole list because we don't want to keep the multi mapping of
+ // UID<->pkg inside MetricsManager to reduce the memory usage.
+ initLogSourceWhiteList();
+ }
+
+ for (const auto& it : mPullAtomPackages) {
+ if (it.second.find(apk) != it.second.end()) {
+ initPullAtomSources();
+ return;
+ }
}
- // We will re-initialize the whole list because we don't want to keep the multi mapping of
- // UID<->pkg inside MetricsManager to reduce the memory usage.
- initLogSourceWhiteList();
}
void MetricsManager::onUidMapReceived(const int64_t& eventTimeNs) {
// Purposefully don't inform metric producers on a new snapshot
// because we don't need to flush partial buckets.
// This occurs if a new user is added/removed or statsd crashes.
+ initPullAtomSources();
+
if (mAllowedPkg.size() == 0) {
return;
}
initLogSourceWhiteList();
}
+void MetricsManager::onStatsdInitCompleted(const int64_t& eventTimeNs) {
+ // Inform all metric producers.
+ for (const auto& it : mAllMetricProducers) {
+ it->onStatsdInitCompleted(eventTimeNs);
+ }
+}
+
+void MetricsManager::init() {
+ for (const auto& producer : mAllMetricProducers) {
+ producer->prepareFirstBucket();
+ }
+}
+
+vector<int32_t> MetricsManager::getPullAtomUids(int32_t atomId) {
+ std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
+ vector<int32_t> uids;
+ const auto& it = mCombinedPullAtomUids.find(atomId);
+ if (it != mCombinedPullAtomUids.end()) {
+ uids.insert(uids.end(), it->second.begin(), it->second.end());
+ }
+ uids.insert(uids.end(), mDefaultPullUids.begin(), mDefaultPullUids.end());
+ return uids;
+}
+
void MetricsManager::dumpStates(FILE* out, bool verbose) {
fprintf(out, "ConfigKey %s, allowed source:", mConfigKey.ToString().c_str());
{
@@ -265,16 +360,18 @@ void MetricsManager::onDumpReport(const int64_t dumpTimeStampNs,
protoOutput->end(token);
}
- mLastReportTimeNs = dumpTimeStampNs;
- mLastReportWallClockNs = getWallClockNs();
+ // Do not update the timestamps when data is not cleared to avoid timestamps from being
+ // misaligned.
+ if (erase_data) {
+ mLastReportTimeNs = dumpTimeStampNs;
+ mLastReportWallClockNs = getWallClockNs();
+ }
VLOG("=========================Metric Reports End==========================");
}
bool MetricsManager::checkLogCredentials(const LogEvent& event) {
- if (android::util::AtomsInfo::kWhitelistedAtoms.find(event.GetTagId()) !=
- android::util::AtomsInfo::kWhitelistedAtoms.end())
- {
+ if (mWhitelistedAtomIds.find(event.GetTagId()) != mWhitelistedAtomIds.end()) {
return true;
}
std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
@@ -286,18 +383,21 @@ bool MetricsManager::checkLogCredentials(const LogEvent& event) {
}
bool MetricsManager::eventSanityCheck(const LogEvent& event) {
- if (event.GetTagId() == android::util::APP_BREADCRUMB_REPORTED) {
+ if (event.GetTagId() == util::APP_BREADCRUMB_REPORTED) {
// Check that app breadcrumb reported fields are valid.
status_t err = NO_ERROR;
// Uid is 3rd from last field and must match the caller's uid,
// unless that caller is statsd itself (statsd is allowed to spoof uids).
long appHookUid = event.GetLong(event.size()-2, &err);
- if (err != NO_ERROR ) {
+ if (err != NO_ERROR) {
VLOG("APP_BREADCRUMB_REPORTED had error when parsing the uid");
return false;
}
- int32_t loggerUid = event.GetUid();
+
+ // Because the uid within the LogEvent may have been mapped from
+ // isolated to host, map the loggerUid similarly before comparing.
+ int32_t loggerUid = mUidMap->getHostUidOrSelf(event.GetUid());
if (loggerUid != appHookUid && loggerUid != AID_STATSD) {
VLOG("APP_BREADCRUMB_REPORTED has invalid uid: claimed %ld but caller is %d",
appHookUid, loggerUid);
@@ -306,21 +406,21 @@ bool MetricsManager::eventSanityCheck(const LogEvent& event) {
// The state must be from 0,3. This part of code must be manually updated.
long appHookState = event.GetLong(event.size(), &err);
- if (err != NO_ERROR ) {
+ if (err != NO_ERROR) {
VLOG("APP_BREADCRUMB_REPORTED had error when parsing the state field");
return false;
} else if (appHookState < 0 || appHookState > 3) {
VLOG("APP_BREADCRUMB_REPORTED does not have valid state %ld", appHookState);
return false;
}
- } else if (event.GetTagId() == android::util::DAVEY_OCCURRED) {
+ } else if (event.GetTagId() == util::DAVEY_OCCURRED) {
// Daveys can be logged from any app since they are logged in libs/hwui/JankTracker.cpp.
// Check that the davey duration is reasonable. Max length check is for privacy.
status_t err = NO_ERROR;
// Uid is the first field provided.
long jankUid = event.GetLong(1, &err);
- if (err != NO_ERROR ) {
+ if (err != NO_ERROR) {
VLOG("Davey occurred had error when parsing the uid");
return false;
}
@@ -332,7 +432,7 @@ bool MetricsManager::eventSanityCheck(const LogEvent& event) {
}
long duration = event.GetLong(event.size(), &err);
- if (err != NO_ERROR ) {
+ if (err != NO_ERROR) {
VLOG("Davey occurred had error when parsing the duration");
return false;
} else if (duration > 100000) {
@@ -555,8 +655,40 @@ void MetricsManager::writeActiveConfigToProtoOutputStream(
}
}
+bool MetricsManager::writeMetadataToProto(int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs,
+ metadata::StatsMetadata* statsMetadata) {
+ bool metadataWritten = false;
+ metadata::ConfigKey* configKey = statsMetadata->mutable_config_key();
+ configKey->set_config_id(mConfigKey.GetId());
+ configKey->set_uid(mConfigKey.GetUid());
+ for (const auto& anomalyTracker : mAllAnomalyTrackers) {
+ metadata::AlertMetadata* alertMetadata = statsMetadata->add_alert_metadata();
+ bool alertWritten = anomalyTracker->writeAlertMetadataToProto(currentWallClockTimeNs,
+ systemElapsedTimeNs, alertMetadata);
+ if (!alertWritten) {
+ statsMetadata->mutable_alert_metadata()->RemoveLast();
+ }
+ metadataWritten |= alertWritten;
+ }
+ return metadataWritten;
+}
-
+void MetricsManager::loadMetadata(const metadata::StatsMetadata& metadata,
+ int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs) {
+ for (const metadata::AlertMetadata& alertMetadata : metadata.alert_metadata()) {
+ int64_t alertId = alertMetadata.alert_id();
+ auto it = mAlertTrackerMap.find(alertId);
+ if (it == mAlertTrackerMap.end()) {
+ ALOGE("No anomalyTracker found for alertId %lld", (long long) alertId);
+ continue;
+ }
+ mAllAnomalyTrackers[it->second]->loadAlertMetadata(alertMetadata,
+ currentWallClockTimeNs,
+ systemElapsedTimeNs);
+ }
+}
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 34d47d426235..ad30a88c5d19 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -23,6 +23,7 @@
#include "config/ConfigKey.h"
#include "external/StatsPullerManager.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h"
#include "logd/LogEvent.h"
#include "matchers/LogMatchingTracker.h"
#include "metrics/MetricProducer.h"
@@ -35,7 +36,7 @@ namespace os {
namespace statsd {
// A MetricsManager is responsible for managing metrics from one single config source.
-class MetricsManager : public virtual android::RefBase {
+class MetricsManager : public virtual android::RefBase, public virtual PullUidProvider {
public:
MetricsManager(const ConfigKey& configKey, const StatsdConfig& config, const int64_t timeBaseNs,
const int64_t currentTimeNs, const sp<UidMap>& uidMap,
@@ -69,6 +70,12 @@ public:
void onUidMapReceived(const int64_t& eventTimeNs);
+ void onStatsdInitCompleted(const int64_t& elapsedTimeNs);
+
+ void init();
+
+ vector<int32_t> getPullAtomUids(int32_t atomId) override;
+
bool shouldWriteToDisk() const {
return mNoReportMetricIds.size() != mAllMetricProducers.size();
}
@@ -139,6 +146,14 @@ public:
void writeActiveConfigToProtoOutputStream(
int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
+ // Returns true if at least one piece of metadata is written.
+ bool writeMetadataToProto(int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs,
+ metadata::StatsMetadata* statsMetadata);
+
+ void loadMetadata(const metadata::StatsMetadata& metadata,
+ int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs);
private:
// For test only.
inline int64_t getTtlEndNs() const { return mTtlEndNs; }
@@ -159,6 +174,8 @@ private:
int64_t mLastReportTimeNs;
int64_t mLastReportWallClockNs;
+ sp<StatsPullerManager> mPullerManager;
+
// The uid log sources from StatsdConfig.
std::vector<int32_t> mAllowedUid;
@@ -169,13 +186,29 @@ private:
// Logs from uids that are not in the list will be ignored to avoid spamming.
std::set<int32_t> mAllowedLogSources;
+ // To guard access to mAllowedLogSources
+ mutable std::mutex mAllowedLogSourcesMutex;
+
+ const std::set<int32_t> mWhitelistedAtomIds;
+
+ // We can pull any atom from these uids.
+ std::set<int32_t> mDefaultPullUids;
+
+ // Uids that specific atoms can pull from.
+ // This is a map<atom id, set<uids>>
+ std::map<int32_t, std::set<int32_t>> mPullAtomUids;
+
+ // Packages that specific atoms can be pulled from.
+ std::map<int32_t, std::set<std::string>> mPullAtomPackages;
+
+ // All uids to pull for this atom. NOTE: Does not include the default uids for memory.
+ std::map<int32_t, std::set<int32_t>> mCombinedPullAtomUids;
+
// Contains the annotations passed in with StatsdConfig.
std::list<std::pair<const int64_t, const int32_t>> mAnnotations;
const bool mShouldPersistHistory;
- // To guard access to mAllowedLogSources
- mutable std::mutex mAllowedLogSourcesMutex;
// All event tags that are interesting to my metrics.
std::set<int> mTagIds;
@@ -230,10 +263,16 @@ private:
// Maps deactivation triggering event to MetricProducers.
std::unordered_map<int, std::vector<int>> mDeactivationAtomTrackerToMetricMap;
+ // Maps AlertIds to the index of the corresponding AnomalyTracker stored in mAllAnomalyTrackers.
+ // The map is used in LoadMetadata to more efficiently lookup AnomalyTrackers from an AlertId.
+ std::unordered_map<int64_t, int> mAlertTrackerMap;
+
std::vector<int> mMetricIndexesWithActivation;
void initLogSourceWhiteList();
+ void initPullAtomSources();
+
// The metrics that don't need to be uploaded or even reported.
std::set<int64_t> mNoReportMetricIds;
@@ -253,23 +292,12 @@ private:
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation);
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition);
FRIEND_TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation);
- FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_NoLink_OR_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_Link_OR_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_OR_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_OR_CombinationCondition);
-
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_SimpleCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_SimpleCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_SimpleCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_AND_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_AND_CombinationCondition);
FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket);
FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets);
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_written);
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk);
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_load_refractory_from_disk);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period);
@@ -282,6 +310,8 @@ private:
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
+ FRIEND_TEST(MetricsManagerTest, TestLogSources);
+
FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
@@ -289,12 +319,31 @@ private:
TestActivationOnBootMultipleActivationsDifferentActivationTypes);
FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
+ FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges);
+ FRIEND_TEST(CountMetricE2eTest, TestSlicedState);
+ FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap);
+ FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates);
+ FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields);
+
FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
FRIEND_TEST(DurationMetricE2eTest, TestWithCondition);
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition);
FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedState);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStateMapped);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSuperset);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset);
+
+ FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation);
+ FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState);
+ FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions);
+ FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index fa310dc707ec..5987a723a421 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -46,19 +46,22 @@ const int FIELD_ID_VALUE_METRICS = 7;
const int FIELD_ID_TIME_BASE = 9;
const int FIELD_ID_BUCKET_SIZE = 10;
const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
-const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
const int FIELD_ID_IS_ACTIVE = 14;
// for ValueMetricDataWrapper
const int FIELD_ID_DATA = 1;
const int FIELD_ID_SKIPPED = 2;
+// for SkippedBuckets
const int FIELD_ID_SKIPPED_START_MILLIS = 3;
const int FIELD_ID_SKIPPED_END_MILLIS = 4;
+const int FIELD_ID_SKIPPED_DROP_EVENT = 5;
+// for DumpEvent Proto
+const int FIELD_ID_BUCKET_DROP_REASON = 1;
+const int FIELD_ID_DROP_TIME = 2;
// for ValueMetricData
const int FIELD_ID_DIMENSION_IN_WHAT = 1;
-const int FIELD_ID_DIMENSION_IN_CONDITION = 2;
const int FIELD_ID_BUCKET_INFO = 3;
const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
-const int FIELD_ID_DIMENSION_LEAF_IN_CONDITION = 5;
+const int FIELD_ID_SLICE_BY_STATE = 6;
// for ValueBucketInfo
const int FIELD_ID_VALUE_INDEX = 1;
const int FIELD_ID_VALUE_LONG = 2;
@@ -75,10 +78,17 @@ const Value ZERO_DOUBLE((int64_t)0);
// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
ValueMetricProducer::ValueMetricProducer(
const ConfigKey& key, const ValueMetric& metric, const int conditionIndex,
+ const vector<ConditionState>& initialConditionCache,
const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int64_t timeBaseNs,
- const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager)
- : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, conditionWizard),
+ const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+ const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+ const vector<int>& slicedStateAtoms,
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
+ : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache,
+ conditionWizard, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
+ stateGroupMap),
mWhatMatcherIndex(whatMatcherIndex),
mEventMatcherWizard(matcherWizard),
mPullerManager(pullerManager),
@@ -100,11 +110,11 @@ ValueMetricProducer::ValueMetricProducer(
mSkipZeroDiffOutput(metric.skip_zero_diff_output()),
mUseZeroDefaultBase(metric.use_zero_default_base()),
mHasGlobalBase(false),
- mCurrentBucketIsInvalid(false),
+ mCurrentBucketIsSkipped(false),
mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC
: StatsdStats::kPullMaxDelayNs),
mSplitBucketForAppUpgrade(metric.split_bucket_for_app_upgrade()),
- // Condition timer will be set in prepareFirstBucketLocked.
+ // Condition timer will be set later within the constructor after pulling events
mConditionTimer(false, timeBaseNs) {
int64_t bucketSizeMills = 0;
if (metric.has_bucket()) {
@@ -120,10 +130,7 @@ ValueMetricProducer::ValueMetricProducer(
if (metric.has_dimensions_in_what()) {
translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat);
mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
- }
-
- if (metric.has_dimensions_in_condition()) {
- translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition);
+ mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what());
}
if (metric.links().size() > 0) {
@@ -134,11 +141,16 @@ ValueMetricProducer::ValueMetricProducer(
translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
mMetric2ConditionLinks.push_back(mc);
}
+ mConditionSliced = true;
}
- mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
- mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()) ||
- HasPositionALL(metric.dimensions_in_condition());
+ for (const auto& stateLink : metric.state_link()) {
+ Metric2State ms;
+ ms.stateAtomId = stateLink.state_atom_id();
+ translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields);
+ translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields);
+ mMetric2StateLinks.push_back(ms);
+ }
int64_t numBucketsForward = calcBucketsForwardCount(startTimeNs);
mCurrentBucketNum += numBucketsForward;
@@ -146,7 +158,7 @@ ValueMetricProducer::ValueMetricProducer(
flushIfNeededLocked(startTimeNs);
if (mIsPulled) {
- mPullerManager->RegisterReceiver(mPullTagId, this, getCurrentBucketEndTimeNs(),
+ mPullerManager->RegisterReceiver(mPullTagId, mConfigKey, this, getCurrentBucketEndTimeNs(),
mBucketSizeNs);
}
@@ -155,6 +167,11 @@ ValueMetricProducer::ValueMetricProducer(
// Adjust start for partial bucket
mCurrentBucketStartTimeNs = startTimeNs;
mConditionTimer.newBucketStart(mCurrentBucketStartTimeNs);
+
+ // Now that activations are processed, start the condition timer if needed.
+ mConditionTimer.onConditionChanged(mIsActive && mCondition == ConditionState::kTrue,
+ mCurrentBucketStartTimeNs);
+
VLOG("value metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
(long long)mBucketSizeNs, (long long)mTimeBaseNs);
}
@@ -162,18 +179,48 @@ ValueMetricProducer::ValueMetricProducer(
ValueMetricProducer::~ValueMetricProducer() {
VLOG("~ValueMetricProducer() called");
if (mIsPulled) {
- mPullerManager->UnRegisterReceiver(mPullTagId, this);
+ mPullerManager->UnRegisterReceiver(mPullTagId, mConfigKey, this);
}
}
-void ValueMetricProducer::prepareFirstBucketLocked() {
- // Kicks off the puller immediately if condition is true and diff based.
- if (mIsActive && mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) {
- pullAndMatchEventsLocked(mCurrentBucketStartTimeNs, mCondition);
+void ValueMetricProducer::onStateChanged(int64_t eventTimeNs, int32_t atomId,
+ const HashableDimensionKey& primaryKey,
+ const FieldValue& oldState, const FieldValue& newState) {
+ VLOG("ValueMetric %lld onStateChanged time %lld, State %d, key %s, %d -> %d",
+ (long long)mMetricId, (long long)eventTimeNs, atomId, primaryKey.toString().c_str(),
+ oldState.mValue.int_value, newState.mValue.int_value);
+
+ // If old and new states are in the same StateGroup, then we do not need to
+ // pull for this state change.
+ FieldValue oldStateCopy = oldState;
+ FieldValue newStateCopy = newState;
+ mapStateValue(atomId, &oldStateCopy);
+ mapStateValue(atomId, &newStateCopy);
+ if (oldStateCopy == newStateCopy) {
+ return;
}
- // Now that activations are processed, start the condition timer if needed.
- mConditionTimer.onConditionChanged(mIsActive && mCondition == ConditionState::kTrue,
- mCurrentBucketStartTimeNs);
+
+ // If condition is not true or metric is not active, we do not need to pull
+ // for this state change.
+ if (mCondition != ConditionState::kTrue || !mIsActive) {
+ return;
+ }
+
+ bool isEventLate = eventTimeNs < mCurrentBucketStartTimeNs;
+ if (isEventLate) {
+ VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
+ (long long)mCurrentBucketStartTimeNs);
+ invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET);
+ return;
+ }
+ mStateChangePrimaryKey.first = atomId;
+ mStateChangePrimaryKey.second = primaryKey;
+ if (mIsPulled) {
+ pullAndMatchEventsLocked(eventTimeNs);
+ }
+ mStateChangePrimaryKey.first = 0;
+ mStateChangePrimaryKey.second = DEFAULT_DIMENSION_KEY;
+ flushIfNeededLocked(eventTimeNs);
}
void ValueMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
@@ -183,11 +230,9 @@ void ValueMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition
void ValueMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
StatsdStats::getInstance().noteBucketDropped(mMetricId);
- // We are going to flush the data without doing a pull first so we need to invalidte the data.
- bool pullNeeded = mIsPulled && mCondition == ConditionState::kTrue;
- if (pullNeeded) {
- invalidateCurrentBucket();
- }
+
+ // The current partial bucket is not flushed and does not require a pull,
+ // so the data is still valid.
flushIfNeededLocked(dropTimeNs);
clearPastBucketsLocked(dropTimeNs);
}
@@ -213,10 +258,10 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
if (pullNeeded) {
switch (dumpLatency) {
case FAST:
- invalidateCurrentBucket();
+ invalidateCurrentBucket(dumpTimeNs, BucketDropReason::DUMP_REPORT_REQUESTED);
break;
case NO_TIME_CONSTRAINTS:
- pullAndMatchEventsLocked(dumpTimeNs, mCondition);
+ pullAndMatchEventsLocked(dumpTimeNs);
break;
}
}
@@ -238,23 +283,26 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
protoOutput->end(dimenPathToken);
}
- if (!mDimensionsInCondition.empty()) {
- uint64_t dimenPathToken =
- protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_CONDITION);
- writeDimensionPathToProto(mDimensionsInCondition, protoOutput);
- protoOutput->end(dimenPathToken);
- }
}
uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS);
- for (const auto& pair : mSkippedBuckets) {
+ for (const auto& skippedBucket : mSkippedBuckets) {
uint64_t wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SKIPPED);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_START_MILLIS,
- (long long)(NanoToMillis(pair.first)));
+ (long long)(NanoToMillis(skippedBucket.bucketStartTimeNs)));
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_END_MILLIS,
- (long long)(NanoToMillis(pair.second)));
+ (long long)(NanoToMillis(skippedBucket.bucketEndTimeNs)));
+ for (const auto& dropEvent : skippedBucket.dropEvents) {
+ uint64_t dropEventToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+ FIELD_ID_SKIPPED_DROP_EVENT);
+ protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_BUCKET_DROP_REASON, dropEvent.reason);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DROP_TIME,
+ (long long)(NanoToMillis(dropEvent.dropTimeNs)));
+ ;
+ protoOutput->end(dropEventToken);
+ }
protoOutput->end(wrapperToken);
}
@@ -270,21 +318,17 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
protoOutput->end(dimensionToken);
- if (dimensionKey.hasDimensionKeyInCondition()) {
- uint64_t dimensionInConditionToken =
- protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
- writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), str_set,
- protoOutput);
- protoOutput->end(dimensionInConditionToken);
- }
} else {
writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
- if (dimensionKey.hasDimensionKeyInCondition()) {
- writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInCondition(),
- FIELD_ID_DIMENSION_LEAF_IN_CONDITION, str_set,
- protoOutput);
- }
+ }
+
+ // Then fill slice_by_state.
+ for (auto state : dimensionKey.getStateValuesKey().getValues()) {
+ uint64_t stateToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+ FIELD_ID_SLICE_BY_STATE);
+ writeStateToProto(state, protoOutput);
+ protoOutput->end(stateToken);
}
// Then fill bucket_info (ValueBucketInfo).
@@ -306,7 +350,7 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_TRUE_NS,
(long long)bucket.mConditionTrueNs);
}
- for (int i = 0; i < (int)bucket.valueIndex.size(); i ++) {
+ for (int i = 0; i < (int)bucket.valueIndex.size(); i++) {
int index = bucket.valueIndex[i];
const Value& value = bucket.values[i];
uint64_t valueToken = protoOutput->start(
@@ -341,23 +385,34 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
}
}
-void ValueMetricProducer::invalidateCurrentBucketWithoutResetBase() {
- if (!mCurrentBucketIsInvalid) {
- // Only report once per invalid bucket.
+void ValueMetricProducer::invalidateCurrentBucketWithoutResetBase(const int64_t dropTimeNs,
+ const BucketDropReason reason) {
+ if (!mCurrentBucketIsSkipped) {
+ // Only report to StatsdStats once per invalid bucket.
StatsdStats::getInstance().noteInvalidatedBucket(mMetricId);
}
- mCurrentBucketIsInvalid = true;
+
+ skipCurrentBucket(dropTimeNs, reason);
}
-void ValueMetricProducer::invalidateCurrentBucket() {
- invalidateCurrentBucketWithoutResetBase();
+void ValueMetricProducer::invalidateCurrentBucket(const int64_t dropTimeNs,
+ const BucketDropReason reason) {
+ invalidateCurrentBucketWithoutResetBase(dropTimeNs, reason);
resetBase();
}
+void ValueMetricProducer::skipCurrentBucket(const int64_t dropTimeNs,
+ const BucketDropReason reason) {
+ if (!maxDropEventsReached()) {
+ mCurrentSkippedBucket.dropEvents.emplace_back(buildDropEvent(dropTimeNs, reason));
+ }
+ mCurrentBucketIsSkipped = true;
+}
+
void ValueMetricProducer::resetBase() {
- for (auto& slice : mCurrentSlicedBucket) {
- for (auto& interval : slice.second) {
- interval.hasBase = false;
+ for (auto& slice : mCurrentBaseInfo) {
+ for (auto& baseInfo : slice.second) {
+ baseInfo.hasBase = false;
}
}
mHasGlobalBase = false;
@@ -369,9 +424,10 @@ void ValueMetricProducer::resetBase() {
// - ConditionTimer tracks changes based on AND of condition and active state.
void ValueMetricProducer::onActiveStateChangedLocked(const int64_t& eventTimeNs) {
bool isEventTooLate = eventTimeNs < mCurrentBucketStartTimeNs;
- if (ConditionState::kTrue == mCondition && isEventTooLate) {
+ if (isEventTooLate) {
// Drop bucket because event arrived too late, ie. we are missing data for this bucket.
- invalidateCurrentBucket();
+ StatsdStats::getInstance().noteLateLogEventSkipped(mMetricId);
+ invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET);
}
// Call parent method once we've verified the validity of current bucket.
@@ -384,7 +440,7 @@ void ValueMetricProducer::onActiveStateChangedLocked(const int64_t& eventTimeNs)
// Pull on active state changes.
if (!isEventTooLate) {
if (mIsPulled) {
- pullAndMatchEventsLocked(eventTimeNs, mCondition);
+ pullAndMatchEventsLocked(eventTimeNs);
}
// When active state changes from true to false, clear diff base but don't
// reset other counters as we may accumulate more value in the bucket.
@@ -404,65 +460,80 @@ void ValueMetricProducer::onConditionChangedLocked(const bool condition,
ConditionState newCondition = condition ? ConditionState::kTrue : ConditionState::kFalse;
bool isEventTooLate = eventTimeNs < mCurrentBucketStartTimeNs;
- if (mIsActive) {
- if (isEventTooLate) {
- VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
- (long long)mCurrentBucketStartTimeNs);
- StatsdStats::getInstance().noteConditionChangeInNextBucket(mMetricId);
- invalidateCurrentBucket();
- } else {
- if (mCondition == ConditionState::kUnknown) {
- // If the condition was unknown, we mark the bucket as invalid since the bucket will
- // contain partial data. For instance, the condition change might happen close to
- // the end of the bucket and we might miss lots of data.
- //
- // We still want to pull to set the base.
- invalidateCurrentBucket();
- }
+ // If the config is not active, skip the event.
+ if (!mIsActive) {
+ mCondition = isEventTooLate ? ConditionState::kUnknown : newCondition;
+ return;
+ }
- // Pull on condition changes.
- bool conditionChanged =
- (mCondition == ConditionState::kTrue && newCondition == ConditionState::kFalse)
- || (mCondition == ConditionState::kFalse &&
- newCondition == ConditionState::kTrue);
- // We do not need to pull when we go from unknown to false.
- //
- // We also pull if the condition was already true in order to be able to flush the
- // bucket at the end if needed.
- //
- // onConditionChangedLocked might happen on bucket boundaries if this is called before
- // #onDataPulled.
- if (mIsPulled && (conditionChanged || condition)) {
- pullAndMatchEventsLocked(eventTimeNs, newCondition);
- }
+ // If the event arrived late, mark the bucket as invalid and skip the event.
+ if (isEventTooLate) {
+ VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
+ (long long)mCurrentBucketStartTimeNs);
+ StatsdStats::getInstance().noteLateLogEventSkipped(mMetricId);
+ StatsdStats::getInstance().noteConditionChangeInNextBucket(mMetricId);
+ invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET);
+ mCondition = ConditionState::kUnknown;
+ mConditionTimer.onConditionChanged(mCondition, eventTimeNs);
+ return;
+ }
- // When condition change from true to false, clear diff base but don't
- // reset other counters as we may accumulate more value in the bucket.
- if (mUseDiff && mCondition == ConditionState::kTrue
- && newCondition == ConditionState::kFalse) {
- resetBase();
- }
- }
+ // If the previous condition was unknown, mark the bucket as invalid
+ // because the bucket will contain partial data. For example, the condition
+ // change might happen close to the end of the bucket and we might miss a
+ // lot of data.
+ //
+ // We still want to pull to set the base.
+ if (mCondition == ConditionState::kUnknown) {
+ invalidateCurrentBucket(eventTimeNs, BucketDropReason::CONDITION_UNKNOWN);
}
- mCondition = isEventTooLate ? initialCondition(mConditionTrackerIndex) : newCondition;
+ // Pull and match for the following condition change cases:
+ // unknown/false -> true - condition changed
+ // true -> false - condition changed
+ // true -> true - old condition was true so we can flush the bucket at the
+ // end if needed.
+ //
+ // We don’t need to pull for unknown -> false or false -> false.
+ //
+ // onConditionChangedLocked might happen on bucket boundaries if this is
+ // called before #onDataPulled.
+ if (mIsPulled &&
+ (newCondition == ConditionState::kTrue || mCondition == ConditionState::kTrue)) {
+ pullAndMatchEventsLocked(eventTimeNs);
+ }
- if (mIsActive) {
- flushIfNeededLocked(eventTimeNs);
- mConditionTimer.onConditionChanged(mCondition, eventTimeNs);
+ // For metrics that use diff, when condition changes from true to false,
+ // clear diff base but don't reset other counts because we may accumulate
+ // more value in the bucket.
+ if (mUseDiff &&
+ (mCondition == ConditionState::kTrue && newCondition == ConditionState::kFalse)) {
+ resetBase();
}
+
+ // Update condition state after pulling.
+ mCondition = newCondition;
+
+ flushIfNeededLocked(eventTimeNs);
+ mConditionTimer.onConditionChanged(mCondition, eventTimeNs);
}
-void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs,
- ConditionState condition) {
+void ValueMetricProducer::prepareFirstBucketLocked() {
+ // Kicks off the puller immediately if condition is true and diff based.
+ if (mIsActive && mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) {
+ pullAndMatchEventsLocked(mCurrentBucketStartTimeNs);
+ }
+}
+
+void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
vector<std::shared_ptr<LogEvent>> allData;
- if (!mPullerManager->Pull(mPullTagId, &allData)) {
+ if (!mPullerManager->Pull(mPullTagId, mConfigKey, timestampNs, &allData)) {
ALOGE("Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
- invalidateCurrentBucket();
+ invalidateCurrentBucket(timestampNs, BucketDropReason::PULL_FAILED);
return;
}
- accumulateEvents(allData, timestampNs, timestampNs, condition);
+ accumulateEvents(allData, timestampNs, timestampNs);
}
int64_t ValueMetricProducer::calcPreviousBucketEndTime(const int64_t currentTimeNs) {
@@ -475,33 +546,33 @@ int64_t ValueMetricProducer::calcPreviousBucketEndTime(const int64_t currentTime
void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData,
bool pullSuccess, int64_t originalPullTimeNs) {
std::lock_guard<std::mutex> lock(mMutex);
- if (mCondition == ConditionState::kTrue) {
- // If the pull failed, we won't be able to compute a diff.
- if (!pullSuccess) {
- invalidateCurrentBucket();
+ if (mCondition == ConditionState::kTrue) {
+ // If the pull failed, we won't be able to compute a diff.
+ if (!pullSuccess) {
+ invalidateCurrentBucket(originalPullTimeNs, BucketDropReason::PULL_FAILED);
+ } else {
+ bool isEventLate = originalPullTimeNs < getCurrentBucketEndTimeNs();
+ if (isEventLate) {
+ // If the event is late, we are in the middle of a bucket. Just
+ // process the data without trying to snap the data to the nearest bucket.
+ accumulateEvents(allData, originalPullTimeNs, originalPullTimeNs);
} else {
- bool isEventLate = originalPullTimeNs < getCurrentBucketEndTimeNs();
- if (isEventLate) {
- // If the event is late, we are in the middle of a bucket. Just
- // process the data without trying to snap the data to the nearest bucket.
- accumulateEvents(allData, originalPullTimeNs, originalPullTimeNs, mCondition);
- } else {
- // For scheduled pulled data, the effective event time is snap to the nearest
- // bucket end. In the case of waking up from a deep sleep state, we will
- // attribute to the previous bucket end. If the sleep was long but not very
- // long, we will be in the immediate next bucket. Previous bucket may get a
- // larger number as we pull at a later time than real bucket end.
- //
- // If the sleep was very long, we skip more than one bucket before sleep. In
- // this case, if the diff base will be cleared and this new data will serve as
- // new diff base.
- int64_t bucketEndTime = calcPreviousBucketEndTime(originalPullTimeNs) - 1;
- StatsdStats::getInstance().noteBucketBoundaryDelayNs(
- mMetricId, originalPullTimeNs - bucketEndTime);
- accumulateEvents(allData, originalPullTimeNs, bucketEndTime, mCondition);
- }
+ // For scheduled pulled data, the effective event time is snap to the nearest
+ // bucket end. In the case of waking up from a deep sleep state, we will
+ // attribute to the previous bucket end. If the sleep was long but not very
+ // long, we will be in the immediate next bucket. Previous bucket may get a
+ // larger number as we pull at a later time than real bucket end.
+ //
+ // If the sleep was very long, we skip more than one bucket before sleep. In
+ // this case, if the diff base will be cleared and this new data will serve as
+ // new diff base.
+ int64_t bucketEndTime = calcPreviousBucketEndTime(originalPullTimeNs) - 1;
+ StatsdStats::getInstance().noteBucketBoundaryDelayNs(
+ mMetricId, originalPullTimeNs - bucketEndTime);
+ accumulateEvents(allData, originalPullTimeNs, bucketEndTime);
}
}
+ }
// We can probably flush the bucket. Since we used bucketEndTime when calling
// #onMatchedLogEventInternalLocked, the current bucket will not have been flushed.
@@ -509,18 +580,18 @@ void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven
}
void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData,
- int64_t originalPullTimeNs, int64_t eventElapsedTimeNs,
- ConditionState condition) {
+ int64_t originalPullTimeNs, int64_t eventElapsedTimeNs) {
bool isEventLate = eventElapsedTimeNs < mCurrentBucketStartTimeNs;
if (isEventLate) {
VLOG("Skip bucket end pull due to late arrival: %lld vs %lld",
(long long)eventElapsedTimeNs, (long long)mCurrentBucketStartTimeNs);
StatsdStats::getInstance().noteLateLogEventSkipped(mMetricId);
- invalidateCurrentBucket();
+ invalidateCurrentBucket(eventElapsedTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET);
return;
}
- const int64_t pullDelayNs = getElapsedRealtimeNs() - originalPullTimeNs;
+ const int64_t elapsedRealtimeNs = getElapsedRealtimeNs();
+ const int64_t pullDelayNs = elapsedRealtimeNs - originalPullTimeNs;
StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
if (pullDelayNs > mMaxPullDelayNs) {
ALOGE("Pull finish too late for atom %d, longer than %lld", mPullTagId,
@@ -528,15 +599,10 @@ void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<Log
StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId);
// We are missing one pull from the bucket which means we will not have a complete view of
// what's going on.
- invalidateCurrentBucket();
+ invalidateCurrentBucket(eventElapsedTimeNs, BucketDropReason::PULL_DELAYED);
return;
}
- if (allData.size() == 0) {
- VLOG("Data pulled is empty");
- StatsdStats::getInstance().noteEmptyData(mPullTagId);
- }
-
mMatchedMetricDimensionKeys.clear();
for (const auto& data : allData) {
LogEvent localCopy = data->makeCopy();
@@ -546,14 +612,19 @@ void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<Log
onMatchedLogEventLocked(mWhatMatcherIndex, localCopy);
}
}
- // If the new pulled data does not contains some keys we track in our intervals, we need to
- // reset the base.
+ // If a key that is:
+ // 1. Tracked in mCurrentSlicedBucket and
+ // 2. A superset of the current mStateChangePrimaryKey
+ // was not found in the new pulled data (i.e. not in mMatchedDimensionInWhatKeys)
+ // then we need to reset the base.
for (auto& slice : mCurrentSlicedBucket) {
- bool presentInPulledData = mMatchedMetricDimensionKeys.find(slice.first)
- != mMatchedMetricDimensionKeys.end();
- if (!presentInPulledData) {
- for (auto& interval : slice.second) {
- interval.hasBase = false;
+ const auto& whatKey = slice.first.getDimensionKeyInWhat();
+ bool presentInPulledData =
+ mMatchedMetricDimensionKeys.find(whatKey) != mMatchedMetricDimensionKeys.end();
+ if (!presentInPulledData && whatKey.contains(mStateChangePrimaryKey.second)) {
+ auto it = mCurrentBaseInfo.find(whatKey);
+ for (auto& baseInfo : it->second) {
+ baseInfo.hasBase = false;
}
}
}
@@ -567,7 +638,7 @@ void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<Log
// incorrectly compute the diff when mUseZeroDefaultBase is true since an existing key
// might be missing from mCurrentSlicedBucket.
if (hasReachedGuardRailLimit()) {
- invalidateCurrentBucket();
+ invalidateCurrentBucket(eventElapsedTimeNs, BucketDropReason::DIMENSION_GUARDRAIL_REACHED);
mCurrentSlicedBucket.clear();
}
}
@@ -582,10 +653,10 @@ void ValueMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
if (verbose) {
for (const auto& it : mCurrentSlicedBucket) {
for (const auto& interval : it.second) {
- fprintf(out, "\t(what)%s\t(condition)%s (value)%s\n",
- it.first.getDimensionKeyInWhat().toString().c_str(),
- it.first.getDimensionKeyInCondition().toString().c_str(),
- interval.value.toString().c_str());
+ fprintf(out, "\t(what)%s\t(states)%s (value)%s\n",
+ it.first.getDimensionKeyInWhat().toString().c_str(),
+ it.first.getStateValuesKey().toString().c_str(),
+ interval.value.toString().c_str());
}
}
}
@@ -653,6 +724,7 @@ bool getDoubleOrLong(const LogEvent& event, const Matcher& matcher, Value& ret)
ret.setDouble(value.mValue.double_value);
break;
default:
+ return false;
break;
}
return true;
@@ -661,17 +733,30 @@ bool getDoubleOrLong(const LogEvent& event, const Matcher& matcher, Value& ret)
return false;
}
-void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIndex,
- const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey,
- bool condition, const LogEvent& event) {
+void ValueMetricProducer::onMatchedLogEventInternalLocked(
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
+ const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+ const map<int, HashableDimensionKey>& statePrimaryKeys) {
+ auto whatKey = eventKey.getDimensionKeyInWhat();
+ auto stateKey = eventKey.getStateValuesKey();
+
+ // Skip this event if a state changed occurred for a different primary key.
+ auto it = statePrimaryKeys.find(mStateChangePrimaryKey.first);
+ // Check that both the atom id and the primary key are equal.
+ if (it != statePrimaryKeys.end() && it->second != mStateChangePrimaryKey.second) {
+ VLOG("ValueMetric skip event with primary key %s because state change primary key "
+ "is %s",
+ it->second.toString().c_str(), mStateChangePrimaryKey.second.toString().c_str());
+ return;
+ }
+
int64_t eventTimeNs = event.GetElapsedTimestampNs();
if (eventTimeNs < mCurrentBucketStartTimeNs) {
VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
(long long)mCurrentBucketStartTimeNs);
return;
}
- mMatchedMetricDimensionKeys.insert(eventKey);
+ mMatchedMetricDimensionKeys.insert(whatKey);
if (!mIsPulled) {
// We cannot flush without doing a pull first.
@@ -689,17 +774,35 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn
bool shouldSkipForPulledMetric = mIsPulled && !mUseDiff
&& mCondition != ConditionState::kTrue;
if (shouldSkipForPushMetric || shouldSkipForPulledMetric) {
- VLOG("ValueMetric skip event because condition is false");
+ VLOG("ValueMetric skip event because condition is false and we are not using diff (for "
+ "pulled metric)");
return;
}
if (hitGuardRailLocked(eventKey)) {
return;
}
- vector<Interval>& multiIntervals = mCurrentSlicedBucket[eventKey];
- if (multiIntervals.size() < mFieldMatchers.size()) {
+
+ vector<BaseInfo>& baseInfos = mCurrentBaseInfo[whatKey];
+ if (baseInfos.size() < mFieldMatchers.size()) {
+ VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size());
+ baseInfos.resize(mFieldMatchers.size());
+ }
+
+ for (BaseInfo& baseInfo : baseInfos) {
+ if (!baseInfo.hasCurrentState) {
+ baseInfo.currentState = getUnknownStateKey();
+ baseInfo.hasCurrentState = true;
+ }
+ }
+
+ // We need to get the intervals stored with the previous state key so we can
+ // close these value intervals.
+ const auto oldStateKey = baseInfos[0].currentState;
+ vector<Interval>& intervals = mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)];
+ if (intervals.size() < mFieldMatchers.size()) {
VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size());
- multiIntervals.resize(mFieldMatchers.size());
+ intervals.resize(mFieldMatchers.size());
}
// We only use anomaly detection under certain cases.
@@ -712,9 +815,12 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn
for (int i = 0; i < (int)mFieldMatchers.size(); i++) {
const Matcher& matcher = mFieldMatchers[i];
- Interval& interval = multiIntervals[i];
+ BaseInfo& baseInfo = baseInfos[i];
+ Interval& interval = intervals[i];
interval.valueIndex = i;
Value value;
+ baseInfo.hasCurrentState = true;
+ baseInfo.currentState = stateKey;
if (!getDoubleOrLong(event, matcher, value)) {
VLOG("Failed to get value %d from event %s", i, event.ToString().c_str());
StatsdStats::getInstance().noteBadValueType(mMetricId);
@@ -723,60 +829,61 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn
interval.seenNewData = true;
if (mUseDiff) {
- if (!interval.hasBase) {
+ if (!baseInfo.hasBase) {
if (mHasGlobalBase && mUseZeroDefaultBase) {
// The bucket has global base. This key does not.
// Optionally use zero as base.
- interval.base = (value.type == LONG ? ZERO_LONG : ZERO_DOUBLE);
- interval.hasBase = true;
+ baseInfo.base = (value.type == LONG ? ZERO_LONG : ZERO_DOUBLE);
+ baseInfo.hasBase = true;
} else {
// no base. just update base and return.
- interval.base = value;
- interval.hasBase = true;
+ baseInfo.base = value;
+ baseInfo.hasBase = true;
// If we're missing a base, do not use anomaly detection on incomplete data
useAnomalyDetection = false;
- // Continue (instead of return) here in order to set interval.base and
- // interval.hasBase for other intervals
+ // Continue (instead of return) here in order to set baseInfo.base and
+ // baseInfo.hasBase for other baseInfos
continue;
}
}
+
Value diff;
switch (mValueDirection) {
case ValueMetric::INCREASING:
- if (value >= interval.base) {
- diff = value - interval.base;
+ if (value >= baseInfo.base) {
+ diff = value - baseInfo.base;
} else if (mUseAbsoluteValueOnReset) {
diff = value;
} else {
VLOG("Unexpected decreasing value");
StatsdStats::getInstance().notePullDataError(mPullTagId);
- interval.base = value;
+ baseInfo.base = value;
// If we've got bad data, do not use anomaly detection
useAnomalyDetection = false;
continue;
}
break;
case ValueMetric::DECREASING:
- if (interval.base >= value) {
- diff = interval.base - value;
+ if (baseInfo.base >= value) {
+ diff = baseInfo.base - value;
} else if (mUseAbsoluteValueOnReset) {
diff = value;
} else {
VLOG("Unexpected increasing value");
StatsdStats::getInstance().notePullDataError(mPullTagId);
- interval.base = value;
+ baseInfo.base = value;
// If we've got bad data, do not use anomaly detection
useAnomalyDetection = false;
continue;
}
break;
case ValueMetric::ANY:
- diff = value - interval.base;
+ diff = value - baseInfo.base;
break;
default:
break;
}
- interval.base = value;
+ baseInfo.base = value;
value = diff;
}
@@ -806,7 +913,7 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn
// Only trigger the tracker if all intervals are correct
if (useAnomalyDetection) {
// TODO: propgate proper values down stream when anomaly support doubles
- long wholeBucketVal = multiIntervals[0].value.long_value;
+ long wholeBucketVal = intervals[0].value.long_value;
auto prev = mCurrentFullBucket.find(eventKey);
if (prev != mCurrentFullBucket.end()) {
wholeBucketVal += prev->second;
@@ -823,7 +930,7 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn
void ValueMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
if (eventTimeNs < currentBucketEndTimeNs) {
- VLOG("eventTime is %lld, less than next bucket start time %lld", (long long)eventTimeNs,
+ VLOG("eventTime is %lld, less than current bucket end time %lld", (long long)eventTimeNs,
(long long)(currentBucketEndTimeNs));
return;
}
@@ -844,25 +951,39 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
const int64_t& nextBucketStartTimeNs) {
if (mCondition == ConditionState::kUnknown) {
StatsdStats::getInstance().noteBucketUnknownCondition(mMetricId);
+ invalidateCurrentBucketWithoutResetBase(eventTimeNs, BucketDropReason::CONDITION_UNKNOWN);
}
+ VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs,
+ (int)mCurrentSlicedBucket.size());
+
+ int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
+ int64_t bucketEndTime = fullBucketEndTimeNs;
int64_t numBucketsForward = calcBucketsForwardCount(eventTimeNs);
- if (numBucketsForward > 1) {
+
+ // Skip buckets if this is a pulled metric or a pushed metric that is diffed.
+ if (numBucketsForward > 1 && (mIsPulled || mUseDiff)) {
+
VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
StatsdStats::getInstance().noteSkippedForwardBuckets(mMetricId);
// Something went wrong. Maybe the device was sleeping for a long time. It is better
// to mark the current bucket as invalid. The last pull might have been successful through.
- invalidateCurrentBucketWithoutResetBase();
+ invalidateCurrentBucketWithoutResetBase(eventTimeNs,
+ BucketDropReason::MULTIPLE_BUCKETS_SKIPPED);
+ // End the bucket at the next bucket start time so the entire interval is skipped.
+ bucketEndTime = nextBucketStartTimeNs;
+ } else if (eventTimeNs < fullBucketEndTimeNs) {
+ bucketEndTime = eventTimeNs;
}
- VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs,
- (int)mCurrentSlicedBucket.size());
- int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
- int64_t bucketEndTime = eventTimeNs < fullBucketEndTimeNs ? eventTimeNs : fullBucketEndTimeNs;
// Close the current bucket.
int64_t conditionTrueDuration = mConditionTimer.newBucketStart(bucketEndTime);
bool isBucketLargeEnough = bucketEndTime - mCurrentBucketStartTimeNs >= mMinBucketSizeNs;
- if (isBucketLargeEnough && !mCurrentBucketIsInvalid) {
+ if (!isBucketLargeEnough) {
+ skipCurrentBucket(eventTimeNs, BucketDropReason::BUCKET_TOO_SMALL);
+ }
+ if (!mCurrentBucketIsSkipped) {
+ bool bucketHasData = false;
// The current bucket is large enough to keep.
for (const auto& slice : mCurrentSlicedBucket) {
ValueBucket bucket = buildPartialBucket(bucketEndTime, slice.second);
@@ -871,13 +992,34 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
if (bucket.valueIndex.size() > 0) {
auto& bucketList = mPastBuckets[slice.first];
bucketList.push_back(bucket);
+ bucketHasData = true;
}
}
- } else {
- mSkippedBuckets.emplace_back(mCurrentBucketStartTimeNs, bucketEndTime);
+ if (!bucketHasData) {
+ skipCurrentBucket(eventTimeNs, BucketDropReason::NO_DATA);
+ }
+ }
+
+ if (mCurrentBucketIsSkipped) {
+ mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs;
+ mCurrentSkippedBucket.bucketEndTimeNs = bucketEndTime;
+ mSkippedBuckets.emplace_back(mCurrentSkippedBucket);
}
- appendToFullBucket(eventTimeNs, fullBucketEndTimeNs);
+ // This means that the current bucket was not flushed before a forced bucket split.
+ // This can happen if an app update or a dump report with include_current_partial_bucket is
+ // requested before we get a chance to flush the bucket due to receiving new data, either from
+ // the statsd socket or the StatsPullerManager.
+ if (bucketEndTime < nextBucketStartTimeNs) {
+ SkippedBucket bucketInGap;
+ bucketInGap.bucketStartTimeNs = bucketEndTime;
+ bucketInGap.bucketEndTimeNs = nextBucketStartTimeNs;
+ bucketInGap.dropEvents.emplace_back(
+ buildDropEvent(eventTimeNs, BucketDropReason::NO_DATA));
+ mSkippedBuckets.emplace_back(bucketInGap);
+ }
+
+ appendToFullBucket(eventTimeNs > fullBucketEndTimeNs);
initCurrentSlicedBucket(nextBucketStartTimeNs);
// Update the condition timer again, in case we skipped buckets.
mConditionTimer.newBucketStart(nextBucketStartTimeNs);
@@ -927,22 +1069,24 @@ void ValueMetricProducer::initCurrentSlicedBucket(int64_t nextBucketStartTimeNs)
} else {
it++;
}
+ // TODO(b/157655103): remove mCurrentBaseInfo entries when obsolete
}
- mCurrentBucketIsInvalid = false;
+ mCurrentBucketIsSkipped = false;
+ mCurrentSkippedBucket.reset();
+
// If we do not have a global base when the condition is true,
// we will have incomplete bucket for the next bucket.
if (mUseDiff && !mHasGlobalBase && mCondition) {
- mCurrentBucketIsInvalid = false;
+ mCurrentBucketIsSkipped = false;
}
mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
(long long)mCurrentBucketStartTimeNs);
}
-void ValueMetricProducer::appendToFullBucket(int64_t eventTimeNs, int64_t fullBucketEndTimeNs) {
- bool isFullBucketReached = eventTimeNs > fullBucketEndTimeNs;
- if (mCurrentBucketIsInvalid) {
+void ValueMetricProducer::appendToFullBucket(const bool isFullBucketReached) {
+ if (mCurrentBucketIsSkipped) {
if (isFullBucketReached) {
// If the bucket is invalid, we ignore the full bucket since it contains invalid data.
mCurrentFullBucket.clear();
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 784ac64880a3..b359af745c91 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -50,12 +50,18 @@ struct ValueBucket {
// - an alarm set to the end of the bucket
class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
public:
- ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
- const int conditionIndex, const sp<ConditionWizard>& conditionWizard,
- const int whatMatcherIndex,
- const sp<EventMatcherWizard>& matcherWizard,
- const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs,
- const sp<StatsPullerManager>& pullerManager);
+ ValueMetricProducer(
+ const ConfigKey& key, const ValueMetric& valueMetric, const int conditionIndex,
+ const vector<ConditionState>& initialConditionCache,
+ const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
+ const sp<EventMatcherWizard>& matcherWizard, const int pullTagId,
+ const int64_t timeBaseNs, const int64_t startTimeNs,
+ const sp<StatsPullerManager>& pullerManager,
+ const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
+ const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+ eventDeactivationMap = {},
+ const vector<int>& slicedStateAtoms = {},
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
virtual ~ValueMetricProducer();
@@ -64,23 +70,34 @@ public:
bool pullSuccess, int64_t originalPullTimeNs) override;
// ValueMetric needs special logic if it's a pulled atom.
- void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
- const int64_t version) override {
+ void notifyAppUpgrade(const int64_t& eventTimeNs) override {
std::lock_guard<std::mutex> lock(mMutex);
if (!mSplitBucketForAppUpgrade) {
return;
}
- if (mIsPulled && mCondition) {
- pullAndMatchEventsLocked(eventTimeNs, mCondition);
+ if (mIsPulled && mCondition == ConditionState::kTrue) {
+ pullAndMatchEventsLocked(eventTimeNs);
}
flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
};
+ // ValueMetric needs special logic if it's a pulled atom.
+ void onStatsdInitCompleted(const int64_t& eventTimeNs) override {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mIsPulled && mCondition == ConditionState::kTrue) {
+ pullAndMatchEventsLocked(eventTimeNs);
+ }
+ flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
+ };
+
+ void onStateChanged(int64_t eventTimeNs, int32_t atomId, const HashableDimensionKey& primaryKey,
+ const FieldValue& oldState, const FieldValue& newState) override;
+
protected:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition,
- const LogEvent& event) override;
+ const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+ const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
private:
void onDumpReportLocked(const int64_t dumpTimeNs,
@@ -125,8 +142,15 @@ private:
int64_t calcBucketsForwardCount(const int64_t& eventTimeNs) const;
// Mark the data as invalid.
- void invalidateCurrentBucket();
- void invalidateCurrentBucketWithoutResetBase();
+ void invalidateCurrentBucket(const int64_t dropTimeNs, const BucketDropReason reason);
+
+ void invalidateCurrentBucketWithoutResetBase(const int64_t dropTimeNs,
+ const BucketDropReason reason);
+
+ // Skips the current bucket without notifying StatsdStats of the skipped bucket.
+ // This should only be called from #flushCurrentBucketLocked. Otherwise, a future event that
+ // causes the bucket to be invalidated will not notify StatsdStats.
+ void skipCurrentBucket(const int64_t dropTimeNs, const BucketDropReason reason);
const int mWhatMatcherIndex;
@@ -138,7 +162,10 @@ private:
std::vector<Matcher> mFieldMatchers;
// Value fields for matching.
- std::set<MetricDimensionKey> mMatchedMetricDimensionKeys;
+ std::set<HashableDimensionKey> mMatchedMetricDimensionKeys;
+
+ // Holds the atom id, primary key pair from a state change.
+ pair<int32_t, HashableDimensionKey> mStateChangePrimaryKey;
// tagId for pulled data. -1 if this is not pulled
const int mPullTagId;
@@ -150,10 +177,6 @@ private:
typedef struct {
// Index in multi value aggregation.
int valueIndex;
- // Holds current base value of the dimension. Take diff and update if necessary.
- Value base;
- // Whether there is a base to diff to.
- bool hasBase;
// Current value, depending on the aggregation type.
Value value;
// Number of samples collected.
@@ -165,34 +188,46 @@ private:
bool seenNewData = false;
} Interval;
+ typedef struct {
+ // Holds current base value of the dimension. Take diff and update if necessary.
+ Value base;
+ // Whether there is a base to diff to.
+ bool hasBase;
+ // Last seen state value(s).
+ HashableDimensionKey currentState;
+ // Whether this dimensions in what key has a current state key.
+ bool hasCurrentState;
+ } BaseInfo;
+
std::unordered_map<MetricDimensionKey, std::vector<Interval>> mCurrentSlicedBucket;
+ std::unordered_map<HashableDimensionKey, std::vector<BaseInfo>> mCurrentBaseInfo;
+
std::unordered_map<MetricDimensionKey, int64_t> mCurrentFullBucket;
// Save the past buckets and we can clear when the StatsLogReport is dumped.
std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>> mPastBuckets;
- // Pairs of (elapsed start, elapsed end) denoting buckets that were skipped.
- std::list<std::pair<int64_t, int64_t>> mSkippedBuckets;
-
const int64_t mMinBucketSizeNs;
// Util function to check whether the specified dimension hits the guardrail.
bool hitGuardRailLocked(const MetricDimensionKey& newKey);
+
bool hasReachedGuardRailLimit() const;
bool hitFullBucketGuardRailLocked(const MetricDimensionKey& newKey);
- void pullAndMatchEventsLocked(const int64_t timestampNs, ConditionState condition);
+ void pullAndMatchEventsLocked(const int64_t timestampNs);
void accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData,
- int64_t originalPullTimeNs, int64_t eventElapsedTimeNs,
- ConditionState condition);
+ int64_t originalPullTimeNs, int64_t eventElapsedTimeNs);
ValueBucket buildPartialBucket(int64_t bucketEndTime,
const std::vector<Interval>& intervals);
+
void initCurrentSlicedBucket(int64_t nextBucketStartTimeNs);
- void appendToFullBucket(int64_t eventTimeNs, int64_t fullBucketEndTimeNs);
+
+ void appendToFullBucket(const bool isFullBucketReached);
// Reset diff base and mHasGlobalBase
void resetBase();
@@ -225,11 +260,9 @@ private:
// diff against.
bool mHasGlobalBase;
- // Invalid bucket. There was a problem in collecting data in the current bucket so we cannot
- // trust any of the data in this bucket.
- //
- // For instance, one pull failed.
- bool mCurrentBucketIsInvalid;
+ // This is to track whether or not the bucket is skipped for any of the reasons listed in
+ // BucketDropReason, many of which make the bucket potentially invalid.
+ bool mCurrentBucketIsSkipped;
const int64_t mMaxPullDelayNs;
@@ -239,12 +272,10 @@ private:
FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection);
FRIEND_TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange);
- FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade);
FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange);
FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition);
FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition);
FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2);
- FRIEND_TEST(ValueMetricProducerTest, TestBucketIncludingUnknownConditionIsInvalid);
FRIEND_TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet);
FRIEND_TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime);
FRIEND_TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged);
@@ -253,14 +284,8 @@ private:
FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled);
FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
FRIEND_TEST(ValueMetricProducerTest, TestFirstBucket);
- FRIEND_TEST(ValueMetricProducerTest, TestFullBucketResetWhenLastBucketInvalid);
- FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenGuardRailHit);
- FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenInitialPullFailed);
- FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenLastPullFailed);
- FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed);
FRIEND_TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff);
FRIEND_TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff);
- FRIEND_TEST(ValueMetricProducerTest, TestPartialBucketCreated);
FRIEND_TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries);
FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryFalse);
FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue);
@@ -271,15 +296,12 @@ private:
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset);
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
- FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade);
- FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse);
FRIEND_TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled);
FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateAvg);
FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMax);
FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMin);
FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateSum);
FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithCondition);
- FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade);
FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition);
FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded);
FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange);
@@ -288,9 +310,28 @@ private:
FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate);
FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput);
FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedState);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMap);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithCondition);
FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey);
FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase);
FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures);
+
+ FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed);
+ FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed);
+ FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFailed);
+ FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenGuardRailHit);
+ FRIEND_TEST(ValueMetricProducerTest_BucketDrop,
+ TestInvalidBucketWhenAccumulateEventWrongBucket);
+
+ FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestBucketBoundariesOnPartialBucket);
+ FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestFullBucketResetWhenLastBucketInvalid);
+ FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPartialBucketCreated);
+ FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPushedEvents);
+ FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPulledValue);
+ FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPulledValueWhileConditionFalse);
+
friend class ValueMetricProducerTestHelper;
};
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 081e61ed21fa..8d59d1362919 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -56,11 +56,19 @@ struct DurationBucket {
int64_t mDuration;
};
+struct DurationValues {
+ // Recorded duration for current partial bucket.
+ int64_t mDuration;
+
+ // Sum of past partial bucket durations in current full bucket.
+ // Used for anomaly detection.
+ int64_t mDurationFullBucket;
+};
+
class DurationTracker {
public:
DurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
- sp<ConditionWizard> wizard, int conditionIndex,
- const std::vector<Matcher>& dimensionInCondition, bool nesting,
+ sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
int64_t currentBucketStartNs, int64_t currentBucketNum, int64_t startTimeNs,
int64_t bucketSizeNs, bool conditionSliced, bool fullLink,
const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
@@ -70,11 +78,9 @@ public:
mWizard(wizard),
mConditionTrackerIndex(conditionIndex),
mBucketSizeNs(bucketSizeNs),
- mDimensionInCondition(dimensionInCondition),
mNested(nesting),
mCurrentBucketStartTimeNs(currentBucketStartNs),
mDuration(0),
- mDurationFullBucket(0),
mCurrentBucketNum(currentBucketNum),
mStartTimeNs(startTimeNs),
mConditionSliced(conditionSliced),
@@ -83,10 +89,8 @@ public:
virtual ~DurationTracker(){};
- virtual unique_ptr<DurationTracker> clone(const int64_t eventTime) = 0;
-
- virtual void noteStart(const HashableDimensionKey& key, bool condition,
- const int64_t eventTime, const ConditionKey& conditionKey) = 0;
+ virtual void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
+ const ConditionKey& conditionKey) = 0;
virtual void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
const bool stopAll) = 0;
virtual void noteStopAll(const int64_t eventTime) = 0;
@@ -94,6 +98,9 @@ public:
virtual void onSlicedConditionMayChange(bool overallCondition, const int64_t timestamp) = 0;
virtual void onConditionChanged(bool condition, const int64_t timestamp) = 0;
+ virtual void onStateChanged(const int64_t timestamp, const int32_t atomId,
+ const FieldValue& newState) = 0;
+
// Flush stale buckets if needed, and return true if the tracker has no on-going duration
// events, so that the owner can safely remove the tracker.
virtual bool flushIfNeeded(
@@ -112,9 +119,12 @@ public:
// Dump internal states for debugging
virtual void dumpStates(FILE* out, bool verbose) const = 0;
- void setEventKey(const MetricDimensionKey& eventKey) {
- mEventKey = eventKey;
- }
+ virtual int64_t getCurrentStateKeyDuration() const = 0;
+
+ virtual int64_t getCurrentStateKeyFullBucketDuration() const = 0;
+
+ // Replace old value with new value for the given state atom.
+ virtual void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState) = 0;
protected:
int64_t getCurrentBucketEndTimeNs() const {
@@ -143,10 +153,11 @@ protected:
}
}
- void addPastBucketToAnomalyTrackers(const int64_t& bucketValue, const int64_t& bucketNum) {
+ void addPastBucketToAnomalyTrackers(const MetricDimensionKey eventKey,
+ const int64_t& bucketValue, const int64_t& bucketNum) {
for (auto& anomalyTracker : mAnomalyTrackers) {
if (anomalyTracker != nullptr) {
- anomalyTracker->addPastBucket(mEventKey, bucketValue, bucketNum);
+ anomalyTracker->addPastBucket(eventKey, bucketValue, bucketNum);
}
}
}
@@ -167,6 +178,10 @@ protected:
return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
}
+ void setEventKey(const MetricDimensionKey& eventKey) {
+ mEventKey = eventKey;
+ }
+
// A reference to the DurationMetricProducer's config key.
const ConfigKey& mConfigKey;
@@ -180,15 +195,14 @@ protected:
const int64_t mBucketSizeNs;
- const std::vector<Matcher>& mDimensionInCondition;
-
const bool mNested;
int64_t mCurrentBucketStartTimeNs;
int64_t mDuration; // current recorded duration result (for partial bucket)
- int64_t mDurationFullBucket; // Sum of past partial buckets in current full bucket.
+ // Recorded duration results for each state key in the current partial bucket.
+ std::unordered_map<HashableDimensionKey, DurationValues> mStateKeyDurationMap;
int64_t mCurrentBucketNum;
@@ -196,7 +210,6 @@ protected:
const bool mConditionSliced;
- bool mSameConditionDimensionsInTracker;
bool mHasLinksToAllConditionDimensionsInTracker;
std::vector<sp<DurationAnomalyTracker>> mAnomalyTrackers;
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index 6868b8c24c71..ee4e1672411f 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -26,37 +26,14 @@ namespace statsd {
MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id,
const MetricDimensionKey& eventKey,
- sp<ConditionWizard> wizard, int conditionIndex,
- const vector<Matcher>& dimensionInCondition, bool nesting,
+ sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
int64_t currentBucketStartNs, int64_t currentBucketNum,
int64_t startTimeNs, int64_t bucketSizeNs,
bool conditionSliced, bool fullLink,
const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
- : DurationTracker(key, id, eventKey, wizard, conditionIndex, dimensionInCondition, nesting,
- currentBucketStartNs, currentBucketNum, startTimeNs, bucketSizeNs,
- conditionSliced, fullLink, anomalyTrackers) {
- if (mWizard != nullptr) {
- mSameConditionDimensionsInTracker =
- mWizard->equalOutputDimensions(conditionIndex, mDimensionInCondition);
- }
-}
-
-unique_ptr<DurationTracker> MaxDurationTracker::clone(const int64_t eventTime) {
- auto clonedTracker = make_unique<MaxDurationTracker>(*this);
- for (auto it = clonedTracker->mInfos.begin(); it != clonedTracker->mInfos.end();) {
- if (it->second.state != kStopped) {
- it->second.lastStartTime = eventTime;
- it->second.lastDuration = 0;
- it++;
- } else {
- it = clonedTracker->mInfos.erase(it);
- }
- }
- if (clonedTracker->mInfos.empty()) {
- return nullptr;
- } else {
- return clonedTracker;
- }
+ : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
+ currentBucketNum, startTimeNs, bucketSizeNs, conditionSliced, fullLink,
+ anomalyTrackers) {
}
bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
@@ -113,7 +90,6 @@ void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool conditi
}
}
-
void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const int64_t eventTime,
bool forceStop) {
VLOG("MaxDuration: key %s stop", key.toString().c_str());
@@ -252,22 +228,21 @@ void MaxDurationTracker::onSlicedConditionMayChange(bool overallCondition,
if (pair.second.state == kStopped) {
continue;
}
- std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
ConditionState conditionState = mWizard->query(
- mConditionTrackerIndex, pair.second.conditionKeys, mDimensionInCondition,
- !mSameConditionDimensionsInTracker,
- !mHasLinksToAllConditionDimensionsInTracker,
- &conditionDimensionKeySet);
- bool conditionMet =
- (conditionState == ConditionState::kTrue) &&
- (mDimensionInCondition.size() == 0 ||
- conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) !=
- conditionDimensionKeySet.end());
+ mConditionTrackerIndex, pair.second.conditionKeys,
+ !mHasLinksToAllConditionDimensionsInTracker);
+ bool conditionMet = (conditionState == ConditionState::kTrue);
+
VLOG("key: %s, condition: %d", pair.first.toString().c_str(), conditionMet);
noteConditionChanged(pair.first, conditionMet, timestamp);
}
}
+void MaxDurationTracker::onStateChanged(const int64_t timestamp, const int32_t atomId,
+ const FieldValue& newState) {
+ ALOGE("MaxDurationTracker does not handle sliced state changes.");
+}
+
void MaxDurationTracker::onConditionChanged(bool condition, const int64_t timestamp) {
for (auto& pair : mInfos) {
noteConditionChanged(pair.first, condition, timestamp);
@@ -337,6 +312,20 @@ void MaxDurationTracker::dumpStates(FILE* out, bool verbose) const {
fprintf(out, "\t\t current duration %lld\n", (long long)mDuration);
}
+int64_t MaxDurationTracker::getCurrentStateKeyDuration() const {
+ ALOGE("MaxDurationTracker does not handle sliced state changes.");
+ return -1;
+}
+
+int64_t MaxDurationTracker::getCurrentStateKeyFullBucketDuration() const {
+ ALOGE("MaxDurationTracker does not handle sliced state changes.");
+ return -1;
+}
+
+void MaxDurationTracker::updateCurrentStateKey(const int32_t atomId, const FieldValue& newState) {
+ ALOGE("MaxDurationTracker does not handle sliced state changes.");
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index 8e8f2cd6c582..2891c6e1138a 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -30,7 +30,7 @@ class MaxDurationTracker : public DurationTracker {
public:
MaxDurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
sp<ConditionWizard> wizard, int conditionIndex,
- const std::vector<Matcher>& dimensionInCondition, bool nesting,
+ bool nesting,
int64_t currentBucketStartNs, int64_t currentBucketNum,
int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced,
bool fullLink,
@@ -38,8 +38,6 @@ public:
MaxDurationTracker(const MaxDurationTracker& tracker) = default;
- unique_ptr<DurationTracker> clone(const int64_t eventTime) override;
-
void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
const ConditionKey& conditionKey) override;
void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
@@ -56,10 +54,19 @@ public:
void onSlicedConditionMayChange(bool overallCondition, const int64_t timestamp) override;
void onConditionChanged(bool condition, const int64_t timestamp) override;
+ void onStateChanged(const int64_t timestamp, const int32_t atomId,
+ const FieldValue& newState) override;
+
int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
const int64_t currentTimestamp) const override;
void dumpStates(FILE* out, bool verbose) const override;
+ int64_t getCurrentStateKeyDuration() const override;
+
+ int64_t getCurrentStateKeyFullBucketDuration() const override;
+
+ void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState);
+
private:
// Returns true if at least one of the mInfos is started.
bool anyStarted();
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index 956383a99eea..0d49bbc269a3 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -26,27 +26,15 @@ using std::pair;
OringDurationTracker::OringDurationTracker(
const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
- sp<ConditionWizard> wizard, int conditionIndex, const vector<Matcher>& dimensionInCondition,
- bool nesting, int64_t currentBucketStartNs, int64_t currentBucketNum,
- int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced, bool fullLink,
- const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
- : DurationTracker(key, id, eventKey, wizard, conditionIndex, dimensionInCondition, nesting,
- currentBucketStartNs, currentBucketNum, startTimeNs, bucketSizeNs,
- conditionSliced, fullLink, anomalyTrackers),
+ sp<ConditionWizard> wizard, int conditionIndex, bool nesting, int64_t currentBucketStartNs,
+ int64_t currentBucketNum, int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced,
+ bool fullLink, const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
+ : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
+ currentBucketNum, startTimeNs, bucketSizeNs, conditionSliced, fullLink,
+ anomalyTrackers),
mStarted(),
mPaused() {
mLastStartTime = 0;
- if (mWizard != nullptr) {
- mSameConditionDimensionsInTracker =
- mWizard->equalOutputDimensions(conditionIndex, mDimensionInCondition);
- }
-}
-
-unique_ptr<DurationTracker> OringDurationTracker::clone(const int64_t eventTime) {
- auto clonedTracker = make_unique<OringDurationTracker>(*this);
- clonedTracker->mLastStartTime = eventTime;
- clonedTracker->mDuration = 0;
- return clonedTracker;
}
bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
@@ -101,10 +89,14 @@ void OringDurationTracker::noteStop(const HashableDimensionKey& key, const int64
mConditionKeyMap.erase(key);
}
if (mStarted.empty()) {
- mDuration += (timestamp - mLastStartTime);
- detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration + mDurationFullBucket);
- VLOG("record duration %lld, total %lld ", (long long)timestamp - mLastStartTime,
- (long long)mDuration);
+ mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration +=
+ (timestamp - mLastStartTime);
+ detectAndDeclareAnomaly(
+ timestamp, mCurrentBucketNum,
+ getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration());
+ VLOG("record duration %lld, total duration %lld for state key %s",
+ (long long)timestamp - mLastStartTime, (long long)getCurrentStateKeyDuration(),
+ mEventKey.getStateValuesKey().toString().c_str());
}
}
@@ -123,10 +115,14 @@ void OringDurationTracker::noteStop(const HashableDimensionKey& key, const int64
void OringDurationTracker::noteStopAll(const int64_t timestamp) {
if (!mStarted.empty()) {
- mDuration += (timestamp - mLastStartTime);
- VLOG("Oring Stop all: record duration %lld %lld ", (long long)timestamp - mLastStartTime,
- (long long)mDuration);
- detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration + mDurationFullBucket);
+ mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration +=
+ (timestamp - mLastStartTime);
+ VLOG("Oring Stop all: record duration %lld, total duration %lld for state key %s",
+ (long long)timestamp - mLastStartTime, (long long)getCurrentStateKeyDuration(),
+ mEventKey.getStateValuesKey().toString().c_str());
+ detectAndDeclareAnomaly(
+ timestamp, mCurrentBucketNum,
+ getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration());
}
stopAnomalyAlarm(timestamp);
@@ -157,21 +153,36 @@ bool OringDurationTracker::flushCurrentBucket(
// Process the current bucket.
if (mStarted.size() > 0) {
- mDuration += (currentBucketEndTimeNs - mLastStartTime);
- }
- if (mDuration > 0) {
- DurationBucket current_info;
- current_info.mBucketStartNs = mCurrentBucketStartTimeNs;
- current_info.mBucketEndNs = currentBucketEndTimeNs;
- current_info.mDuration = mDuration;
- (*output)[mEventKey].push_back(current_info);
- mDurationFullBucket += mDuration;
- VLOG(" duration: %lld", (long long)current_info.mDuration);
+ // Calculate the duration for the current state key.
+ mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration +=
+ (currentBucketEndTimeNs - mLastStartTime);
}
- if (eventTimeNs > fullBucketEnd) {
- // End of full bucket, can send to anomaly tracker now.
- addPastBucketToAnomalyTrackers(mDurationFullBucket, mCurrentBucketNum);
- mDurationFullBucket = 0;
+ // Store DurationBucket info for each whatKey, stateKey pair.
+ // Note: The whatKey stored in mEventKey is constant for each DurationTracker, while the
+ // stateKey stored in mEventKey is only the current stateKey. mStateKeyDurationMap is used to
+ // store durations for each stateKey, so we need to flush the bucket by creating a
+ // DurationBucket for each stateKey.
+ for (auto& durationIt : mStateKeyDurationMap) {
+ if (durationIt.second.mDuration > 0) {
+ DurationBucket current_info;
+ current_info.mBucketStartNs = mCurrentBucketStartTimeNs;
+ current_info.mBucketEndNs = currentBucketEndTimeNs;
+ current_info.mDuration = durationIt.second.mDuration;
+ (*output)[MetricDimensionKey(mEventKey.getDimensionKeyInWhat(), durationIt.first)]
+ .push_back(current_info);
+
+ durationIt.second.mDurationFullBucket += durationIt.second.mDuration;
+ VLOG(" duration: %lld", (long long)current_info.mDuration);
+ }
+
+ if (eventTimeNs > fullBucketEnd) {
+ // End of full bucket, can send to anomaly tracker now.
+ addPastBucketToAnomalyTrackers(
+ MetricDimensionKey(mEventKey.getDimensionKeyInWhat(), durationIt.first),
+ getCurrentStateKeyFullBucketDuration(), mCurrentBucketNum);
+ durationIt.second.mDurationFullBucket = 0;
+ }
+ durationIt.second.mDuration = 0;
}
if (mStarted.size() > 0) {
@@ -180,20 +191,19 @@ bool OringDurationTracker::flushCurrentBucket(
info.mBucketStartNs = fullBucketEnd + mBucketSizeNs * (i - 1);
info.mBucketEndNs = info.mBucketStartNs + mBucketSizeNs;
info.mDuration = mBucketSizeNs;
+ // Full duration buckets are attributed to the current stateKey.
(*output)[mEventKey].push_back(info);
// Safe to send these buckets to anomaly tracker since they must be full buckets.
// If it's a partial bucket, numBucketsForward would be 0.
- addPastBucketToAnomalyTrackers(info.mDuration, mCurrentBucketNum + i);
+ addPastBucketToAnomalyTrackers(mEventKey, info.mDuration, mCurrentBucketNum + i);
VLOG(" add filling bucket with duration %lld", (long long)info.mDuration);
}
} else {
if (numBucketsForward >= 2) {
- addPastBucketToAnomalyTrackers(0, mCurrentBucketNum + numBucketsForward - 1);
+ addPastBucketToAnomalyTrackers(mEventKey, 0, mCurrentBucketNum + numBucketsForward - 1);
}
}
- mDuration = 0;
-
if (numBucketsForward > 0) {
mCurrentBucketStartTimeNs = fullBucketEnd + (numBucketsForward - 1) * mBucketSizeNs;
mCurrentBucketNum += numBucketsForward;
@@ -227,17 +237,10 @@ void OringDurationTracker::onSlicedConditionMayChange(bool overallCondition,
++it;
continue;
}
- std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
ConditionState conditionState =
mWizard->query(mConditionTrackerIndex, condIt->second,
- mDimensionInCondition,
- !mSameConditionDimensionsInTracker,
- !mHasLinksToAllConditionDimensionsInTracker,
- &conditionDimensionKeySet);
- if (conditionState != ConditionState::kTrue ||
- (mDimensionInCondition.size() != 0 &&
- conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) ==
- conditionDimensionKeySet.end())) {
+ !mHasLinksToAllConditionDimensionsInTracker);
+ if (conditionState != ConditionState::kTrue) {
startedToPaused.push_back(*it);
it = mStarted.erase(it);
VLOG("Key %s started -> paused", key.toString().c_str());
@@ -247,10 +250,14 @@ void OringDurationTracker::onSlicedConditionMayChange(bool overallCondition,
}
if (mStarted.empty()) {
- mDuration += (timestamp - mLastStartTime);
- VLOG("Duration add %lld , to %lld ", (long long)(timestamp - mLastStartTime),
- (long long)mDuration);
- detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration + mDurationFullBucket);
+ mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration +=
+ (timestamp - mLastStartTime);
+ VLOG("record duration %lld, total duration %lld for state key %s",
+ (long long)(timestamp - mLastStartTime), (long long)getCurrentStateKeyDuration(),
+ mEventKey.getStateValuesKey().toString().c_str());
+ detectAndDeclareAnomaly(
+ timestamp, mCurrentBucketNum,
+ getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration());
}
}
@@ -262,17 +269,10 @@ void OringDurationTracker::onSlicedConditionMayChange(bool overallCondition,
++it;
continue;
}
- std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
ConditionState conditionState =
mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key],
- mDimensionInCondition,
- !mSameConditionDimensionsInTracker,
- !mHasLinksToAllConditionDimensionsInTracker,
- &conditionDimensionKeySet);
- if (conditionState == ConditionState::kTrue &&
- (mDimensionInCondition.size() == 0 ||
- conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) !=
- conditionDimensionKeySet.end())) {
+ !mHasLinksToAllConditionDimensionsInTracker);
+ if (conditionState == ConditionState::kTrue) {
pausedToStarted.push_back(*it);
it = mPaused.erase(it);
VLOG("Key %s paused -> started", key.toString().c_str());
@@ -313,10 +313,13 @@ void OringDurationTracker::onConditionChanged(bool condition, const int64_t time
} else {
if (!mStarted.empty()) {
VLOG("Condition false, all paused");
- mDuration += (timestamp - mLastStartTime);
+ mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration +=
+ (timestamp - mLastStartTime);
mPaused.insert(mStarted.begin(), mStarted.end());
mStarted.clear();
- detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration + mDurationFullBucket);
+ detectAndDeclareAnomaly(
+ timestamp, mCurrentBucketNum,
+ getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration());
}
}
if (mStarted.empty()) {
@@ -324,6 +327,23 @@ void OringDurationTracker::onConditionChanged(bool condition, const int64_t time
}
}
+void OringDurationTracker::onStateChanged(const int64_t timestamp, const int32_t atomId,
+ const FieldValue& newState) {
+ // Nothing needs to be done on a state change if we have not seen a start
+ // event, the metric is currently not active, or condition is false.
+ // For these cases, no keys are being tracked in mStarted, so update
+ // the current state key and return.
+ if (mStarted.empty()) {
+ updateCurrentStateKey(atomId, newState);
+ return;
+ }
+ // Add the current duration length to the previous state key and then update
+ // the last start time and current state key.
+ mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration += (timestamp - mLastStartTime);
+ mLastStartTime = timestamp;
+ updateCurrentStateKey(atomId, newState);
+}
+
int64_t OringDurationTracker::predictAnomalyTimestampNs(
const DurationAnomalyTracker& anomalyTracker, const int64_t eventTimestampNs) const {
@@ -333,12 +353,13 @@ int64_t OringDurationTracker::predictAnomalyTimestampNs(
// The timestamp of the current bucket end.
const int64_t currentBucketEndNs = getCurrentBucketEndTimeNs();
- // The past duration ns for the current bucket.
- int64_t currentBucketPastNs = mDuration + mDurationFullBucket;
+ // The past duration ns for the current bucket of the current stateKey.
+ int64_t currentStateBucketPastNs =
+ getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration();
// As we move into the future, old buckets get overwritten (so their old data is erased).
// Sum of past durations. Will change as we overwrite old buckets.
- int64_t pastNs = currentBucketPastNs + anomalyTracker.getSumOverPastBuckets(mEventKey);
+ int64_t pastNs = currentStateBucketPastNs + anomalyTracker.getSumOverPastBuckets(mEventKey);
// The refractory period end timestamp for dimension mEventKey.
const int64_t refractoryPeriodEndNs =
@@ -397,7 +418,7 @@ int64_t OringDurationTracker::predictAnomalyTimestampNs(
mEventKey,
mCurrentBucketNum - anomalyTracker.getNumOfPastBuckets() + futureBucketIdx);
} else if (futureBucketIdx == anomalyTracker.getNumOfPastBuckets()) {
- pastNs -= (currentBucketPastNs + (currentBucketEndNs - eventTimestampNs));
+ pastNs -= (currentStateBucketPastNs + (currentBucketEndNs - eventTimestampNs));
}
}
@@ -407,7 +428,34 @@ int64_t OringDurationTracker::predictAnomalyTimestampNs(
void OringDurationTracker::dumpStates(FILE* out, bool verbose) const {
fprintf(out, "\t\t started count %lu\n", (unsigned long)mStarted.size());
fprintf(out, "\t\t paused count %lu\n", (unsigned long)mPaused.size());
- fprintf(out, "\t\t current duration %lld\n", (long long)mDuration);
+ fprintf(out, "\t\t current duration %lld\n", (long long)getCurrentStateKeyDuration());
+}
+
+int64_t OringDurationTracker::getCurrentStateKeyDuration() const {
+ auto it = mStateKeyDurationMap.find(mEventKey.getStateValuesKey());
+ if (it == mStateKeyDurationMap.end()) {
+ return 0;
+ } else {
+ return it->second.mDuration;
+ }
+}
+
+int64_t OringDurationTracker::getCurrentStateKeyFullBucketDuration() const {
+ auto it = mStateKeyDurationMap.find(mEventKey.getStateValuesKey());
+ if (it == mStateKeyDurationMap.end()) {
+ return 0;
+ } else {
+ return it->second.mDurationFullBucket;
+ }
+}
+
+void OringDurationTracker::updateCurrentStateKey(const int32_t atomId, const FieldValue& newState) {
+ HashableDimensionKey* stateValuesKey = mEventKey.getMutableStateValuesKey();
+ for (size_t i = 0; i < stateValuesKey->getValues().size(); i++) {
+ if (stateValuesKey->getValues()[i].mField.getTag() == atomId) {
+ stateValuesKey->mutableValue(i)->mValue = newState.mValue;
+ }
+ }
}
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index e46616969116..bd8017a7decd 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -28,16 +28,13 @@ class OringDurationTracker : public DurationTracker {
public:
OringDurationTracker(const ConfigKey& key, const int64_t& id,
const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard,
- int conditionIndex, const std::vector<Matcher>& dimensionInCondition,
- bool nesting, int64_t currentBucketStartNs, int64_t currentBucketNum,
- int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced,
- bool fullLink,
+ int conditionIndex, bool nesting, int64_t currentBucketStartNs,
+ int64_t currentBucketNum, int64_t startTimeNs, int64_t bucketSizeNs,
+ bool conditionSliced, bool fullLink,
const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers);
OringDurationTracker(const OringDurationTracker& tracker) = default;
- unique_ptr<DurationTracker> clone(const int64_t eventTime) override;
-
void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
const ConditionKey& conditionKey) override;
void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
@@ -47,6 +44,9 @@ public:
void onSlicedConditionMayChange(bool overallCondition, const int64_t timestamp) override;
void onConditionChanged(bool condition, const int64_t timestamp) override;
+ void onStateChanged(const int64_t timestamp, const int32_t atomId,
+ const FieldValue& newState) override;
+
bool flushCurrentBucket(
const int64_t& eventTimeNs,
std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
@@ -58,6 +58,12 @@ public:
const int64_t currentTimestamp) const override;
void dumpStates(FILE* out, bool verbose) const override;
+ int64_t getCurrentStateKeyDuration() const override;
+
+ int64_t getCurrentStateKeyFullBucketDuration() const override;
+
+ void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState);
+
private:
// We don't need to keep track of individual durations. The information that's needed is:
// 1) which keys are started. We record the first start time.
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 45c3c69b04b2..8917c36bb608 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -19,24 +19,24 @@
#include "metrics_manager_util.h"
-#include "../condition/CombinationConditionTracker.h"
-#include "../condition/SimpleConditionTracker.h"
-#include "../condition/StateTracker.h"
-#include "../external/StatsPullerManager.h"
-#include "../matchers/CombinationLogMatchingTracker.h"
-#include "../matchers/SimpleLogMatchingTracker.h"
-#include "../matchers/EventMatcherWizard.h"
-#include "../metrics/CountMetricProducer.h"
-#include "../metrics/DurationMetricProducer.h"
-#include "../metrics/EventMetricProducer.h"
-#include "../metrics/GaugeMetricProducer.h"
-#include "../metrics/ValueMetricProducer.h"
-
-#include "atoms_info.h"
-#include "stats_util.h"
-
#include <inttypes.h>
+#include "FieldValue.h"
+#include "MetricProducer.h"
+#include "condition/CombinationConditionTracker.h"
+#include "condition/SimpleConditionTracker.h"
+#include "external/StatsPullerManager.h"
+#include "matchers/CombinationLogMatchingTracker.h"
+#include "matchers/EventMatcherWizard.h"
+#include "matchers/SimpleLogMatchingTracker.h"
+#include "metrics/CountMetricProducer.h"
+#include "metrics/DurationMetricProducer.h"
+#include "metrics/EventMetricProducer.h"
+#include "metrics/GaugeMetricProducer.h"
+#include "metrics/ValueMetricProducer.h"
+#include "state/StateManager.h"
+#include "stats_util.h"
+
using std::set;
using std::unordered_map;
using std::vector;
@@ -136,6 +136,105 @@ bool handleMetricWithConditions(
return true;
}
+// Initializes state data structures for a metric.
+// input:
+// [config]: the input config
+// [stateIds]: the slice_by_state ids for this metric
+// [stateAtomIdMap]: this map contains the mapping from all state ids to atom ids
+// [allStateGroupMaps]: this map contains the mapping from state ids and state
+// values to state group ids for all states
+// output:
+// [slicedStateAtoms]: a vector of atom ids of all the slice_by_states
+// [stateGroupMap]: this map should contain the mapping from states ids and state
+// values to state group ids for all states that this metric
+// is interested in
+bool handleMetricWithStates(
+ const StatsdConfig& config, const ::google::protobuf::RepeatedField<int64_t>& stateIds,
+ const unordered_map<int64_t, int>& stateAtomIdMap,
+ const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+ vector<int>& slicedStateAtoms,
+ unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) {
+ for (const auto& stateId : stateIds) {
+ auto it = stateAtomIdMap.find(stateId);
+ if (it == stateAtomIdMap.end()) {
+ ALOGW("cannot find State %" PRId64 " in the config", stateId);
+ return false;
+ }
+ int atomId = it->second;
+ slicedStateAtoms.push_back(atomId);
+
+ auto stateIt = allStateGroupMaps.find(stateId);
+ if (stateIt != allStateGroupMaps.end()) {
+ stateGroupMap[atomId] = stateIt->second;
+ }
+ }
+ return true;
+}
+
+bool handleMetricWithStateLink(const FieldMatcher& stateMatcher,
+ const vector<Matcher>& dimensionsInWhat) {
+ vector<Matcher> stateMatchers;
+ translateFieldMatcher(stateMatcher, &stateMatchers);
+
+ return subsetDimensions(stateMatchers, dimensionsInWhat);
+}
+
+// Validates a metricActivation and populates state.
+// EventActivationMap and EventDeactivationMap are supplied to a MetricProducer
+// to provide the producer with state about its activators and deactivators.
+// Returns false if there are errors.
+bool handleMetricActivation(
+ const StatsdConfig& config,
+ const int64_t metricId,
+ const int metricIndex,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ const unordered_map<int64_t, int>& logTrackerMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation,
+ unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+ unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) {
+ // Check if metric has an associated activation
+ auto itr = metricToActivationMap.find(metricId);
+ if (itr == metricToActivationMap.end()) return true;
+
+ int activationIndex = itr->second;
+ const MetricActivation& metricActivation = config.metric_activation(activationIndex);
+
+ for (int i = 0; i < metricActivation.event_activation_size(); i++) {
+ const EventActivation& activation = metricActivation.event_activation(i);
+
+ auto itr = logTrackerMap.find(activation.atom_matcher_id());
+ if (itr == logTrackerMap.end()) {
+ ALOGE("Atom matcher not found for event activation.");
+ return false;
+ }
+
+ ActivationType activationType = (activation.has_activation_type()) ?
+ activation.activation_type() : metricActivation.activation_type();
+ std::shared_ptr<Activation> activationWrapper = std::make_shared<Activation>(
+ activationType, activation.ttl_seconds() * NS_PER_SEC);
+
+ int atomMatcherIndex = itr->second;
+ activationAtomTrackerToMetricMap[atomMatcherIndex].push_back(metricIndex);
+ eventActivationMap.emplace(atomMatcherIndex, activationWrapper);
+
+ if (activation.has_deactivation_atom_matcher_id()) {
+ itr = logTrackerMap.find(activation.deactivation_atom_matcher_id());
+ if (itr == logTrackerMap.end()) {
+ ALOGE("Atom matcher not found for event deactivation.");
+ return false;
+ }
+ int deactivationAtomMatcherIndex = itr->second;
+ deactivationAtomTrackerToMetricMap[deactivationAtomMatcherIndex].push_back(metricIndex);
+ eventDeactivationMap[deactivationAtomMatcherIndex].push_back(activationWrapper);
+ }
+ }
+
+ metricsWithActivation.push_back(metricIndex);
+ return true;
+}
+
bool initLogTrackers(const StatsdConfig& config, const UidMap& uidMap,
unordered_map<int64_t, int>& logTrackerMap,
vector<sp<LogMatchingTracker>>& allAtomMatchers, set<int>& allTagIds) {
@@ -182,74 +281,26 @@ bool initLogTrackers(const StatsdConfig& config, const UidMap& uidMap,
return true;
}
-/**
- * A StateTracker is built from a SimplePredicate which has only "start", and no "stop"
- * or "stop_all". The start must be an atom matcher that matches a state atom. It must
- * have dimension, the dimension must be the state atom's primary fields plus exclusive state
- * field. For example, the StateTracker is used in tracking UidProcessState and ScreenState.
- *
- */
-bool isStateTracker(const SimplePredicate& simplePredicate, vector<Matcher>* primaryKeys) {
- // 1. must not have "stop". must have "dimension"
- if (!simplePredicate.has_stop() && simplePredicate.has_dimensions()) {
- auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(
- simplePredicate.dimensions().field());
- // 2. must be based on a state atom.
- if (it != android::util::AtomsInfo::kStateAtomsFieldOptions.end()) {
- // 3. dimension must be primary fields + state field IN ORDER
- size_t expectedDimensionCount = it->second.primaryFields.size() + 1;
- vector<Matcher> dimensions;
- translateFieldMatcher(simplePredicate.dimensions(), &dimensions);
- if (dimensions.size() != expectedDimensionCount) {
- return false;
- }
- // 3.1 check the primary fields first.
- size_t index = 0;
- for (const auto& field : it->second.primaryFields) {
- Matcher matcher = getSimpleMatcher(it->first, field);
- if (!(matcher == dimensions[index])) {
- return false;
- }
- primaryKeys->push_back(matcher);
- index++;
- }
- Matcher stateFieldMatcher =
- getSimpleMatcher(it->first, it->second.exclusiveField);
- // 3.2 last dimension should be the exclusive field.
- if (!(dimensions.back() == stateFieldMatcher)) {
- return false;
- }
- return true;
- }
- }
- return false;
-} // namespace statsd
-
bool initConditions(const ConfigKey& key, const StatsdConfig& config,
const unordered_map<int64_t, int>& logTrackerMap,
unordered_map<int64_t, int>& conditionTrackerMap,
vector<sp<ConditionTracker>>& allConditionTrackers,
- unordered_map<int, std::vector<int>>& trackerToConditionMap) {
+ unordered_map<int, std::vector<int>>& trackerToConditionMap,
+ vector<ConditionState>& initialConditionCache) {
vector<Predicate> conditionConfigs;
const int conditionTrackerCount = config.predicate_size();
conditionConfigs.reserve(conditionTrackerCount);
allConditionTrackers.reserve(conditionTrackerCount);
+ initialConditionCache.reserve(conditionTrackerCount);
+ std::fill(initialConditionCache.begin(), initialConditionCache.end(), ConditionState::kUnknown);
for (int i = 0; i < conditionTrackerCount; i++) {
const Predicate& condition = config.predicate(i);
int index = allConditionTrackers.size();
switch (condition.contents_case()) {
case Predicate::ContentsCase::kSimplePredicate: {
- vector<Matcher> primaryKeys;
- if (isStateTracker(condition.simple_predicate(), &primaryKeys)) {
- allConditionTrackers.push_back(new StateTracker(key, condition.id(), index,
- condition.simple_predicate(),
- logTrackerMap, primaryKeys));
- } else {
- allConditionTrackers.push_back(new SimpleConditionTracker(
- key, condition.id(), index, condition.simple_predicate(),
- logTrackerMap));
- }
+ allConditionTrackers.push_back(new SimpleConditionTracker(
+ key, condition.id(), index, condition.simple_predicate(), logTrackerMap));
break;
}
case Predicate::ContentsCase::kCombination: {
@@ -273,7 +324,7 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config,
for (size_t i = 0; i < allConditionTrackers.size(); i++) {
auto& conditionTracker = allConditionTrackers[i];
if (!conditionTracker->init(conditionConfigs, allConditionTrackers, conditionTrackerMap,
- stackTracker)) {
+ stackTracker, initialConditionCache)) {
return false;
}
for (const int trackerIndex : conditionTracker->getLogTrackerIndex()) {
@@ -284,23 +335,59 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config,
return true;
}
+bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
+ unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps) {
+ for (int i = 0; i < config.state_size(); i++) {
+ const State& state = config.state(i);
+ const int64_t stateId = state.id();
+ stateAtomIdMap[stateId] = state.atom_id();
+
+ const StateMap& stateMap = state.map();
+ for (auto group : stateMap.group()) {
+ for (auto value : group.value()) {
+ allStateGroupMaps[stateId][value] = group.group_id();
+ }
+ }
+ }
+
+ return true;
+}
+
bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs,
- const int64_t currentTimeNs,
- const sp<StatsPullerManager>& pullerManager,
+ const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
const unordered_map<int64_t, int>& logTrackerMap,
const unordered_map<int64_t, int>& conditionTrackerMap,
const vector<sp<LogMatchingTracker>>& allAtomMatchers,
+ const unordered_map<int64_t, int>& stateAtomIdMap,
+ const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
vector<sp<ConditionTracker>>& allConditionTrackers,
+ const vector<ConditionState>& initialConditionCache,
vector<sp<MetricProducer>>& allMetricProducers,
- unordered_map<int, std::vector<int>>& conditionToMetricMap,
- unordered_map<int, std::vector<int>>& trackerToMetricMap,
- unordered_map<int64_t, int>& metricMap, std::set<int64_t>& noReportMetricIds) {
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int64_t, int>& metricMap, std::set<int64_t>& noReportMetricIds,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchers);
const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
- config.event_metric_size() + config.value_metric_size();
+ config.event_metric_size() + config.gauge_metric_size() +
+ config.value_metric_size();
allMetricProducers.reserve(allMetricsCount);
- StatsPullerManager statsPullerManager;
+
+ // Construct map from metric id to metric activation index. The map will be used to determine
+ // the metric activation corresponding to a metric.
+ unordered_map<int64_t, int> metricToActivationMap;
+ for (int i = 0; i < config.metric_activation_size(); i++) {
+ const MetricActivation& metricActivation = config.metric_activation(i);
+ int64_t metricId = metricActivation.metric_id();
+ if (metricToActivationMap.find(metricId) != metricToActivationMap.end()) {
+ ALOGE("Metric %lld has multiple MetricActivations", (long long) metricId);
+ return false;
+ }
+ metricToActivationMap.insert({metricId, i});
+ }
// Build MetricProducers for each metric defined in config.
// build CountMetricProducer
@@ -323,10 +410,9 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
int conditionIndex = -1;
if (metric.has_condition()) {
- bool good = handleMetricWithConditions(
- metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
- allConditionTrackers, conditionIndex, conditionToMetricMap);
- if (!good) {
+ if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+ metric.links(), allConditionTrackers, conditionIndex,
+ conditionToMetricMap)) {
return false;
}
} else {
@@ -336,8 +422,32 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
}
}
+ std::vector<int> slicedStateAtoms;
+ unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
+ if (metric.slice_by_state_size() > 0) {
+ if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
+ allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
+ return false;
+ }
+ } else {
+ if (metric.state_link_size() > 0) {
+ ALOGW("CountMetric has a MetricStateLink but doesn't have a slice_by_state");
+ return false;
+ }
+ }
+
+ unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+ bool success = handleMetricActivation(config, metric.id(), metricIndex,
+ metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
+ eventDeactivationMap);
+ if (!success) return false;
+
sp<MetricProducer> countProducer =
- new CountMetricProducer(key, metric, conditionIndex, wizard, timeBaseTimeNs, currentTimeNs);
+ new CountMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
+ timeBaseTimeNs, currentTimeNs, eventActivationMap,
+ eventDeactivationMap, slicedStateAtoms, stateGroupMap);
allMetricProducers.push_back(countProducer);
}
@@ -405,9 +515,46 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
}
}
+ std::vector<int> slicedStateAtoms;
+ unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
+ if (metric.slice_by_state_size() > 0) {
+ if (metric.aggregation_type() == DurationMetric::MAX_SPARSE) {
+ ALOGE("DurationMetric with aggregation type MAX_SPARSE cannot be sliced by state");
+ return false;
+ }
+ if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
+ allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
+ return false;
+ }
+ } else {
+ if (metric.state_link_size() > 0) {
+ ALOGW("DurationMetric has a MetricStateLink but doesn't have a sliced state");
+ return false;
+ }
+ }
+
+ // Check that all metric state links are a subset of dimensions_in_what fields.
+ std::vector<Matcher> dimensionsInWhat;
+ translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
+ for (const auto& stateLink : metric.state_link()) {
+ if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) {
+ return false;
+ }
+ }
+
+ unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+ bool success = handleMetricActivation(config, metric.id(), metricIndex,
+ metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
+ eventDeactivationMap);
+ if (!success) return false;
+
sp<MetricProducer> durationMetric = new DurationMetricProducer(
- key, metric, conditionIndex, trackerIndices[0], trackerIndices[1],
- trackerIndices[2], nesting, wizard, internalDimensions, timeBaseTimeNs, currentTimeNs);
+ key, metric, conditionIndex, initialConditionCache, trackerIndices[0],
+ trackerIndices[1], trackerIndices[2], nesting, wizard, internalDimensions,
+ timeBaseTimeNs, currentTimeNs, eventActivationMap, eventDeactivationMap,
+ slicedStateAtoms, stateGroupMap);
allMetricProducers.push_back(durationMetric);
}
@@ -442,8 +589,17 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
}
}
+ unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+ bool success = handleMetricActivation(config, metric.id(), metricIndex,
+ metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
+ eventDeactivationMap);
+ if (!success) return false;
+
sp<MetricProducer> eventMetric =
- new EventMetricProducer(key, metric, conditionIndex, wizard, timeBaseTimeNs);
+ new EventMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
+ timeBaseTimeNs, eventActivationMap, eventDeactivationMap);
allMetricProducers.push_back(eventMetric);
}
@@ -482,7 +638,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
return false;
}
int atomTagId = *(atomMatcher->getAtomIds().begin());
- int pullTagId = statsPullerManager.PullerForMatcherExists(atomTagId) ? atomTagId : -1;
+ int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
int conditionIndex = -1;
if (metric.has_condition()) {
@@ -499,9 +655,41 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
}
}
+ std::vector<int> slicedStateAtoms;
+ unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
+ if (metric.slice_by_state_size() > 0) {
+ if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
+ allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
+ return false;
+ }
+ } else {
+ if (metric.state_link_size() > 0) {
+ ALOGW("ValueMetric has a MetricStateLink but doesn't have a sliced state");
+ return false;
+ }
+ }
+
+ // Check that all metric state links are a subset of dimensions_in_what fields.
+ std::vector<Matcher> dimensionsInWhat;
+ translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
+ for (const auto& stateLink : metric.state_link()) {
+ if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) {
+ return false;
+ }
+ }
+
+ unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+ bool success = handleMetricActivation(
+ config, metric.id(), metricIndex, metricToActivationMap, logTrackerMap,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, eventActivationMap, eventDeactivationMap);
+ if (!success) return false;
+
sp<MetricProducer> valueProducer = new ValueMetricProducer(
- key, metric, conditionIndex, wizard, trackerIndex, matcherWizard, pullTagId,
- timeBaseTimeNs, currentTimeNs, pullerManager);
+ key, metric, conditionIndex, initialConditionCache, wizard, trackerIndex,
+ matcherWizard, pullTagId, timeBaseTimeNs, currentTimeNs, pullerManager,
+ eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap);
allMetricProducers.push_back(valueProducer);
}
@@ -542,7 +730,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
return false;
}
int atomTagId = *(atomMatcher->getAtomIds().begin());
- int pullTagId = statsPullerManager.PullerForMatcherExists(atomTagId) ? atomTagId : -1;
+ int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
int triggerTrackerIndex;
int triggerAtomId = -1;
@@ -585,10 +773,18 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
}
}
+ unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+ bool success = handleMetricActivation(config, metric.id(), metricIndex,
+ metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
+ eventDeactivationMap);
+ if (!success) return false;
+
sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(
- key, metric, conditionIndex, wizard,
- trackerIndex, matcherWizard, pullTagId, triggerAtomId, atomTagId,
- timeBaseTimeNs, currentTimeNs, pullerManager);
+ key, metric, conditionIndex, initialConditionCache, wizard, trackerIndex,
+ matcherWizard, pullTagId, triggerAtomId, atomTagId, timeBaseTimeNs, currentTimeNs,
+ pullerManager, eventActivationMap, eventDeactivationMap);
allMetricProducers.push_back(gaugeProducer);
}
for (int i = 0; i < config.no_report_metric_size(); ++i) {
@@ -599,15 +795,30 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
}
noReportMetricIds.insert(no_report_metric);
}
+
+ const set<int> whitelistedAtomIds(config.whitelisted_atom_ids().begin(),
+ config.whitelisted_atom_ids().end());
+ for (const auto& it : allMetricProducers) {
+ // Register metrics to StateTrackers
+ for (int atomId : it->getSlicedStateAtoms()) {
+ // Register listener for non-whitelisted atoms only. Using whitelisted atom as a sliced
+ // state atom is not allowed.
+ if (whitelistedAtomIds.find(atomId) == whitelistedAtomIds.end()) {
+ StateManager::getInstance().registerListener(atomId, it);
+ } else {
+ return false;
+ }
+ }
+ }
return true;
}
bool initAlerts(const StatsdConfig& config,
const unordered_map<int64_t, int>& metricProducerMap,
+ unordered_map<int64_t, int>& alertTrackerMap,
const sp<AlarmMonitor>& anomalyAlarmMonitor,
vector<sp<MetricProducer>>& allMetricProducers,
vector<sp<AnomalyTracker>>& allAnomalyTrackers) {
- unordered_map<int64_t, int> anomalyTrackerMap;
for (int i = 0; i < config.alert_size(); i++) {
const Alert& alert = config.alert(i);
const auto& itr = metricProducerMap.find(alert.metric_id());
@@ -632,7 +843,7 @@ bool initAlerts(const StatsdConfig& config,
// The ALOGW for this invalid alert was already displayed in addAnomalyTracker().
return false;
}
- anomalyTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size()));
+ alertTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size()));
allAnomalyTrackers.push_back(anomalyTracker);
}
for (int i = 0; i < config.subscription_size(); ++i) {
@@ -646,8 +857,8 @@ bool initAlerts(const StatsdConfig& config,
(long long)subscription.id());
return false;
}
- const auto& itr = anomalyTrackerMap.find(subscription.rule_id());
- if (itr == anomalyTrackerMap.end()) {
+ const auto& itr = alertTrackerMap.find(subscription.rule_id());
+ if (itr == alertTrackerMap.end()) {
ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"",
(long long)subscription.id(), (long long)subscription.rule_id());
return false;
@@ -703,73 +914,6 @@ bool initAlarms(const StatsdConfig& config, const ConfigKey& key,
return true;
}
-bool initMetricActivations(const ConfigKey& key, const StatsdConfig& config,
- const int64_t currentTimeNs,
- const unordered_map<int64_t, int> &logEventTrackerMap,
- const unordered_map<int64_t, int> &metricProducerMap,
- vector<sp<MetricProducer>>& allMetricProducers,
- unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation) {
- for (int i = 0; i < config.metric_activation_size(); ++i) {
- const MetricActivation& metric_activation = config.metric_activation(i);
- auto itr = metricProducerMap.find(metric_activation.metric_id());
- if (itr == metricProducerMap.end()) {
- ALOGE("Metric id not found in metric activation: %lld",
- (long long)metric_activation.metric_id());
- return false;
- }
- const int metricTrackerIndex = itr->second;
- if (metricTrackerIndex < 0 || metricTrackerIndex >= (int)allMetricProducers.size()) {
- ALOGE("Invalid metric tracker index.");
- return false;
- }
- const sp<MetricProducer>& metric = allMetricProducers[metricTrackerIndex];
- metricsWithActivation.push_back(metricTrackerIndex);
- for (int j = 0; j < metric_activation.event_activation_size(); ++j) {
- const EventActivation& activation = metric_activation.event_activation(j);
- auto logTrackerIt = logEventTrackerMap.find(activation.atom_matcher_id());
- if (logTrackerIt == logEventTrackerMap.end()) {
- ALOGE("Atom matcher not found for event activation.");
- return false;
- }
- const int atomMatcherIndex = logTrackerIt->second;
- activationAtomTrackerToMetricMap[atomMatcherIndex].push_back(
- metricTrackerIndex);
-
- ActivationType activationType;
- if (activation.has_activation_type()) {
- activationType = activation.activation_type();
- } else {
- activationType = metric_activation.activation_type();
- }
-
- if (activation.has_deactivation_atom_matcher_id()) {
- auto deactivationAtomMatcherIt =
- logEventTrackerMap.find(activation.deactivation_atom_matcher_id());
- if (deactivationAtomMatcherIt == logEventTrackerMap.end()) {
- ALOGE("Atom matcher not found for event deactivation.");
- return false;
- }
- const int deactivationMatcherIndex = deactivationAtomMatcherIt->second;
- deactivationAtomTrackerToMetricMap[deactivationMatcherIndex]
- .push_back(metricTrackerIndex);
- metric->addActivation(atomMatcherIndex, activationType, activation.ttl_seconds(),
- deactivationMatcherIndex);
- } else {
- metric->addActivation(atomMatcherIndex, activationType, activation.ttl_seconds());
- }
- }
- }
- return true;
-}
-
-void prepareFirstBucket(const vector<sp<MetricProducer>>& allMetricProducers) {
- for (const auto& metric: allMetricProducers) {
- metric->prepareFirstBucket();
- }
-}
-
bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap,
const sp<StatsPullerManager>& pullerManager,
const sp<AlarmMonitor>& anomalyAlarmMonitor,
@@ -785,11 +929,15 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap&
unordered_map<int, std::vector<int>>& trackerToConditionMap,
unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ unordered_map<int64_t, int>& alertTrackerMap,
vector<int>& metricsWithActivation,
std::set<int64_t>& noReportMetricIds) {
unordered_map<int64_t, int> logTrackerMap;
unordered_map<int64_t, int> conditionTrackerMap;
+ vector<ConditionState> initialConditionCache;
unordered_map<int64_t, int> metricProducerMap;
+ unordered_map<int64_t, int> stateAtomIdMap;
+ unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
if (!initLogTrackers(config, uidMap, logTrackerMap, allAtomMatchers, allTagIds)) {
ALOGE("initLogMatchingTrackers failed");
@@ -798,20 +946,26 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap&
VLOG("initLogMatchingTrackers succeed...");
if (!initConditions(key, config, logTrackerMap, conditionTrackerMap, allConditionTrackers,
- trackerToConditionMap)) {
+ trackerToConditionMap, initialConditionCache)) {
ALOGE("initConditionTrackers failed");
return false;
}
+ if (!initStates(config, stateAtomIdMap, allStateGroupMaps)) {
+ ALOGE("initStates failed");
+ return false;
+ }
if (!initMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager, logTrackerMap,
- conditionTrackerMap, allAtomMatchers, allConditionTrackers, allMetricProducers,
- conditionToMetricMap, trackerToMetricMap, metricProducerMap,
- noReportMetricIds)) {
+ conditionTrackerMap, allAtomMatchers, stateAtomIdMap, allStateGroupMaps,
+ allConditionTrackers, initialConditionCache, allMetricProducers,
+ conditionToMetricMap, trackerToMetricMap, metricProducerMap, noReportMetricIds,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation)) {
ALOGE("initMetricProducers failed");
return false;
}
- if (!initAlerts(config, metricProducerMap, anomalyAlarmMonitor, allMetricProducers,
- allAnomalyTrackers)) {
+ if (!initAlerts(config, metricProducerMap, alertTrackerMap, anomalyAlarmMonitor,
+ allMetricProducers, allAnomalyTrackers)) {
ALOGE("initAlerts failed");
return false;
}
@@ -820,14 +974,6 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap&
ALOGE("initAlarms failed");
return false;
}
- if (!initMetricActivations(key, config, currentTimeNs, logTrackerMap, metricProducerMap,
- allMetricProducers, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
- ALOGE("initMetricActivations failed");
- return false;
- }
-
- prepareFirstBucket(allMetricProducers);
return true;
}
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index ece986b7fcc9..96b5c26ff789 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -60,12 +60,25 @@ bool initLogTrackers(const StatsdConfig& config,
// [allConditionTrackers]: stores the sp to all the ConditionTrackers
// [trackerToConditionMap]: contain the mapping from index of
// log tracker to condition trackers that use the log tracker
+// [initialConditionCache]: stores the initial conditions for each ConditionTracker
bool initConditions(const ConfigKey& key, const StatsdConfig& config,
const std::unordered_map<int64_t, int>& logTrackerMap,
std::unordered_map<int64_t, int>& conditionTrackerMap,
std::vector<sp<ConditionTracker>>& allConditionTrackers,
std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
- std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks);
+ std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks,
+ std::vector<ConditionState>& initialConditionCache);
+
+// Initialize State maps using State protos in the config. These maps will
+// eventually be passed to MetricProducers to initialize their state info.
+// input:
+// [config]: the input config
+// output:
+// [stateAtomIdMap]: this map should contain the mapping from state ids to atom ids
+// [allStateGroupMaps]: this map should contain the mapping from states ids and state
+// values to state group ids for all states
+bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
+ unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps);
// Initialize MetricProducers.
// input:
@@ -74,6 +87,9 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config,
// [timeBaseSec]: start time base for all metrics
// [logTrackerMap]: LogMatchingTracker name to index mapping from previous step.
// [conditionTrackerMap]: condition name to index mapping
+// [stateAtomIdMap]: contains the mapping from state ids to atom ids
+// [allStateGroupMaps]: contains the mapping from atom ids and state values to
+// state group ids for all states
// output:
// [allMetricProducers]: contains the list of sp to the MetricProducers created.
// [conditionToMetricMap]: contains the mapping from condition tracker index to
@@ -86,11 +102,17 @@ bool initMetrics(
const std::unordered_map<int64_t, int>& conditionTrackerMap,
const std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks,
const vector<sp<LogMatchingTracker>>& allAtomMatchers,
+ const unordered_map<int64_t, int>& stateAtomIdMap,
+ const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::vector<ConditionState>& initialConditionCache,
std::vector<sp<MetricProducer>>& allMetricProducers,
std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
- std::set<int64_t>& noReportMetricIds);
+ std::set<int64_t>& noReportMetricIds,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation);
// Initialize MetricsManager from StatsdConfig.
// Parameters are the members of MetricsManager. See MetricsManager for declaration.
@@ -109,11 +131,10 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap&
std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::unordered_map<int64_t, int>& alertTrackerMap,
vector<int>& metricsWithActivation,
std::set<int64_t>& noReportMetricIds);
-bool isStateTracker(const SimplePredicate& simplePredicate, std::vector<Matcher>* primaryKeys);
-
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index ab0e86e24b02..acf40c88a00d 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -545,7 +545,15 @@ const std::map<string, uint32_t> UidMap::sAidToUidMapping = {{"AID_ROOT", 0},
{"AID_LMKD", 1069},
{"AID_LLKD", 1070},
{"AID_IORAPD", 1071},
+ {"AID_GPU_SERVICE", 1072},
{"AID_NETWORK_STACK", 1073},
+ {"AID_GSID", 1074},
+ {"AID_FSVERITY_CERT", 1075},
+ {"AID_CREDSTORE", 1076},
+ {"AID_EXTERNAL_STORAGE", 1077},
+ {"AID_EXT_DATA_RW", 1078},
+ {"AID_EXT_OBB_RW", 1079},
+ {"AID_CONTEXT_HUB", 1080},
{"AID_SHELL", 2000},
{"AID_CACHE", 2001},
{"AID_DIAG", 2002}};
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index bfac6e3431b0..22250aee402e 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -21,10 +21,11 @@
#include "packages/PackageInfoListener.h"
#include "stats_util.h"
-#include <binder/IShellCallback.h>
#include <gtest/gtest_prod.h>
#include <stdio.h>
#include <utils/RefBase.h>
+#include <utils/String16.h>
+
#include <list>
#include <mutex>
#include <set>
@@ -138,7 +139,7 @@ public:
// record is deleted.
void appendUidMap(const int64_t& timestamp, const ConfigKey& key, std::set<string>* str_set,
bool includeVersionStrings, bool includeInstaller,
- util::ProtoOutputStream* proto);
+ ProtoOutputStream* proto);
// Forces the output to be cleared. We still generate a snapshot based on the current state.
// This results in extra data uploaded but helps us reconstruct the uid mapping on the server
@@ -148,7 +149,7 @@ public:
// Get currently cached value of memory used by UID map.
size_t getBytesUsed() const;
- std::set<int32_t> getAppUid(const string& package) const;
+ virtual std::set<int32_t> getAppUid(const string& package) const;
// Write current PackageInfoSnapshot to ProtoOutputStream.
// interestingUids: If not empty, only write the package info for these uids. If empty, write
diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp
index d6a04336bc46..fd883c29dba0 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.cpp
+++ b/cmds/statsd/src/shell/ShellSubscriber.cpp
@@ -18,6 +18,8 @@
#include "ShellSubscriber.h"
+#include <android-base/file.h>
+
#include "matchers/matcher_util.h"
#include "stats_log_util.h"
@@ -29,204 +31,213 @@ namespace statsd {
const static int FIELD_ID_ATOM = 1;
-void ShellSubscriber::startNewSubscription(int in, int out, sp<IResultReceiver> resultReceiver,
- int timeoutSec) {
- VLOG("start new shell subscription");
+void ShellSubscriber::startNewSubscription(int in, int out, int timeoutSec) {
+ int myToken = claimToken();
+ VLOG("ShellSubscriber: new subscription %d has come in", myToken);
+ mSubscriptionShouldEnd.notify_one();
+
+ shared_ptr<SubscriptionInfo> mySubscriptionInfo = make_shared<SubscriptionInfo>(in, out);
+ if (!readConfig(mySubscriptionInfo)) return;
+
{
- std::lock_guard<std::mutex> lock(mMutex);
- if (mResultReceiver != nullptr) {
- VLOG("Only one shell subscriber is allowed.");
- return;
- }
- mInput = in;
- mOutput = out;
- mResultReceiver = resultReceiver;
- IInterface::asBinder(mResultReceiver)->linkToDeath(this);
- }
+ std::unique_lock<std::mutex> lock(mMutex);
+ mSubscriptionInfo = mySubscriptionInfo;
+ spawnHelperThread(myToken);
+ waitForSubscriptionToEndLocked(mySubscriptionInfo, myToken, lock, timeoutSec);
- // Note that the following is blocking, and it's intended as we cannot return until the shell
- // cmd exits, otherwise all resources & FDs will be automatically closed.
+ if (mSubscriptionInfo == mySubscriptionInfo) {
+ mSubscriptionInfo = nullptr;
+ }
- // Read config forever until EOF is reached. Clients may send multiple configs -- each new
- // config replace the previous one.
- readConfig(in);
- VLOG("timeout : %d", timeoutSec);
+ }
+}
- // Now we have read an EOF we now wait for the semaphore until the client exits.
- VLOG("Now wait for client to exit");
- std::unique_lock<std::mutex> lk(mMutex);
+void ShellSubscriber::spawnHelperThread(int myToken) {
+ std::thread t([this, myToken] { pullAndSendHeartbeats(myToken); });
+ t.detach();
+}
+void ShellSubscriber::waitForSubscriptionToEndLocked(shared_ptr<SubscriptionInfo> myInfo,
+ int myToken,
+ std::unique_lock<std::mutex>& lock,
+ int timeoutSec) {
if (timeoutSec > 0) {
- mShellDied.wait_for(lk, timeoutSec * 1s,
- [this, resultReceiver] { return mResultReceiver != resultReceiver; });
+ mSubscriptionShouldEnd.wait_for(lock, timeoutSec * 1s, [this, myToken, &myInfo] {
+ return mToken != myToken || !myInfo->mClientAlive;
+ });
} else {
- mShellDied.wait(lk, [this, resultReceiver] { return mResultReceiver != resultReceiver; });
+ mSubscriptionShouldEnd.wait(lock, [this, myToken, &myInfo] {
+ return mToken != myToken || !myInfo->mClientAlive;
+ });
}
}
-void ShellSubscriber::updateConfig(const ShellSubscription& config) {
- std::lock_guard<std::mutex> lock(mMutex);
- mPushedMatchers.clear();
- mPulledInfo.clear();
+// Atomically claim the next token. Token numbers denote subscriber ordering.
+int ShellSubscriber::claimToken() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ int myToken = ++mToken;
+ return myToken;
+}
- for (const auto& pushed : config.pushed()) {
- mPushedMatchers.push_back(pushed);
- VLOG("adding matcher for atom %d", pushed.atom_id());
+// Read and parse single config. There should only one config per input.
+bool ShellSubscriber::readConfig(shared_ptr<SubscriptionInfo> subscriptionInfo) {
+ // Read the size of the config.
+ size_t bufferSize;
+ if (!android::base::ReadFully(subscriptionInfo->mInputFd, &bufferSize, sizeof(bufferSize))) {
+ return false;
}
- int64_t token = getElapsedRealtimeNs();
- mPullToken = token;
-
- int64_t minInterval = -1;
- for (const auto& pulled : config.pulled()) {
- // All intervals need to be multiples of the min interval.
- if (minInterval < 0 || pulled.freq_millis() < minInterval) {
- minInterval = pulled.freq_millis();
- }
+ // Read the config.
+ vector<uint8_t> buffer(bufferSize);
+ if (!android::base::ReadFully(subscriptionInfo->mInputFd, buffer.data(), bufferSize)) {
+ return false;
+ }
- mPulledInfo.emplace_back(pulled.matcher(), pulled.freq_millis());
- VLOG("adding matcher for pulled atom %d", pulled.matcher().atom_id());
+ // Parse the config.
+ ShellSubscription config;
+ if (!config.ParseFromArray(buffer.data(), bufferSize)) {
+ return false;
}
- if (mPulledInfo.size() > 0 && minInterval > 0) {
- // This thread is guaranteed to terminate after it detects the token is different or
- // cleaned up.
- std::thread puller([token, minInterval, this] { startPull(token, minInterval); });
- puller.detach();
+ // Update SubscriptionInfo with state from config
+ for (const auto& pushed : config.pushed()) {
+ subscriptionInfo->mPushedMatchers.push_back(pushed);
}
-}
-void ShellSubscriber::writeToOutputLocked(const vector<std::shared_ptr<LogEvent>>& data,
- const SimpleAtomMatcher& matcher) {
- if (mOutput == 0) return;
- int count = 0;
- mProto.clear();
- for (const auto& event : data) {
- VLOG("%s", event->ToString().c_str());
- if (matchesSimple(*mUidMap, matcher, *event)) {
- VLOG("matched");
- count++;
- uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
- util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
- event->ToProto(mProto);
- mProto.end(atomToken);
+ for (const auto& pulled : config.pulled()) {
+ vector<string> packages;
+ vector<int32_t> uids;
+ for (const string& pkg : pulled.packages()) {
+ auto it = UidMap::sAidToUidMapping.find(pkg);
+ if (it != UidMap::sAidToUidMapping.end()) {
+ uids.push_back(it->second);
+ } else {
+ packages.push_back(pkg);
+ }
}
- }
- if (count > 0) {
- // First write the payload size.
- size_t bufferSize = mProto.size();
- write(mOutput, &bufferSize, sizeof(bufferSize));
- VLOG("%d atoms, proto size: %zu", count, bufferSize);
- // Then write the payload.
- mProto.flush(mOutput);
+ subscriptionInfo->mPulledInfo.emplace_back(pulled.matcher(), pulled.freq_millis(), packages,
+ uids);
+ VLOG("adding matcher for pulled atom %d", pulled.matcher().atom_id());
}
- mProto.clear();
+
+ return true;
}
-void ShellSubscriber::startPull(int64_t token, int64_t intervalMillis) {
- while (1) {
- int64_t nowMillis = getElapsedRealtimeMillis();
+void ShellSubscriber::pullAndSendHeartbeats(int myToken) {
+ VLOG("ShellSubscriber: helper thread %d starting", myToken);
+ while (true) {
+ int64_t sleepTimeMs = INT_MAX;
{
std::lock_guard<std::mutex> lock(mMutex);
- if (mPulledInfo.size() == 0 || mPullToken != token) {
- VLOG("Pulling thread %lld done!", (long long)token);
+ if (!mSubscriptionInfo || mToken != myToken) {
+ VLOG("ShellSubscriber: helper thread %d done!", myToken);
return;
}
- for (auto& pullInfo : mPulledInfo) {
- if (pullInfo.mPrevPullElapsedRealtimeMs + pullInfo.mInterval < nowMillis) {
- VLOG("pull atom %d now", pullInfo.mPullerMatcher.atom_id());
-
- vector<std::shared_ptr<LogEvent>> data;
- mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), &data);
- VLOG("pulled %zu atoms", data.size());
- if (data.size() > 0) {
- writeToOutputLocked(data, pullInfo.mPullerMatcher);
- }
- pullInfo.mPrevPullElapsedRealtimeMs = nowMillis;
+
+ int64_t nowMillis = getElapsedRealtimeMillis();
+ int64_t nowNanos = getElapsedRealtimeNs();
+ for (PullInfo& pullInfo : mSubscriptionInfo->mPulledInfo) {
+ if (pullInfo.mPrevPullElapsedRealtimeMs + pullInfo.mInterval >= nowMillis) {
+ continue;
}
+
+ vector<int32_t> uids;
+ getUidsForPullAtom(&uids, pullInfo);
+
+ vector<std::shared_ptr<LogEvent>> data;
+ mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), uids, nowNanos, &data);
+ VLOG("Pulled %zu atoms with id %d", data.size(), pullInfo.mPullerMatcher.atom_id());
+ writePulledAtomsLocked(data, pullInfo.mPullerMatcher);
+
+ pullInfo.mPrevPullElapsedRealtimeMs = nowMillis;
}
+
+ // Send a heartbeat, consisting of a data size of 0, if perfd hasn't recently received
+ // data from statsd. When it receives the data size of 0, perfd will not expect any
+ // atoms and recheck whether the subscription should end.
+ if (nowMillis - mLastWriteMs > kMsBetweenHeartbeats) {
+ attemptWriteToPipeLocked(/*dataSize=*/0);
+ }
+
+ // Determine how long to sleep before doing more work.
+ for (PullInfo& pullInfo : mSubscriptionInfo->mPulledInfo) {
+ int64_t nextPullTime = pullInfo.mPrevPullElapsedRealtimeMs + pullInfo.mInterval;
+ int64_t timeBeforePull = nextPullTime - nowMillis; // guaranteed to be non-negative
+ if (timeBeforePull < sleepTimeMs) sleepTimeMs = timeBeforePull;
+ }
+ int64_t timeBeforeHeartbeat = (mLastWriteMs + kMsBetweenHeartbeats) - nowMillis;
+ if (timeBeforeHeartbeat < sleepTimeMs) sleepTimeMs = timeBeforeHeartbeat;
}
- VLOG("Pulling thread %lld sleep....", (long long)token);
- std::this_thread::sleep_for(std::chrono::milliseconds(intervalMillis));
+
+ VLOG("ShellSubscriber: helper thread %d sleeping for %lld ms", myToken,
+ (long long)sleepTimeMs);
+ std::this_thread::sleep_for(std::chrono::milliseconds(sleepTimeMs));
}
}
-void ShellSubscriber::readConfig(int in) {
- if (in <= 0) {
- return;
+void ShellSubscriber::getUidsForPullAtom(vector<int32_t>* uids, const PullInfo& pullInfo) {
+ uids->insert(uids->end(), pullInfo.mPullUids.begin(), pullInfo.mPullUids.end());
+ // This is slow. Consider storing the uids per app and listening to uidmap updates.
+ for (const string& pkg : pullInfo.mPullPackages) {
+ set<int32_t> uidsForPkg = mUidMap->getAppUid(pkg);
+ uids->insert(uids->end(), uidsForPkg.begin(), uidsForPkg.end());
}
+ uids->push_back(DEFAULT_PULL_UID);
+}
- while (1) {
- size_t bufferSize = 0;
- int result = 0;
- if ((result = read(in, &bufferSize, sizeof(bufferSize))) == 0) {
- VLOG("Done reading");
- break;
- } else if (result < 0 || result != sizeof(bufferSize)) {
- ALOGE("Error reading config size");
- break;
- }
-
- vector<uint8_t> buffer(bufferSize);
- if ((result = read(in, buffer.data(), bufferSize)) > 0 && ((size_t)result) == bufferSize) {
- ShellSubscription config;
- if (config.ParseFromArray(buffer.data(), bufferSize)) {
- updateConfig(config);
- } else {
- ALOGE("error parsing the config");
- break;
- }
- } else {
- VLOG("Error reading the config, returned: %d, expecting %zu", result, bufferSize);
- break;
+void ShellSubscriber::writePulledAtomsLocked(const vector<std::shared_ptr<LogEvent>>& data,
+ const SimpleAtomMatcher& matcher) {
+ mProto.clear();
+ int count = 0;
+ for (const auto& event : data) {
+ if (matchesSimple(*mUidMap, matcher, *event)) {
+ count++;
+ uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
+ util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
+ event->ToProto(mProto);
+ mProto.end(atomToken);
}
}
-}
-void ShellSubscriber::cleanUpLocked() {
- // The file descriptors will be closed by binder.
- mInput = 0;
- mOutput = 0;
- mResultReceiver = nullptr;
- mPushedMatchers.clear();
- mPulledInfo.clear();
- mPullToken = 0;
- VLOG("done clean up");
+ if (count > 0) attemptWriteToPipeLocked(mProto.size());
}
void ShellSubscriber::onLogEvent(const LogEvent& event) {
std::lock_guard<std::mutex> lock(mMutex);
+ if (!mSubscriptionInfo) return;
- if (mOutput <= 0) {
- return;
- }
- for (const auto& matcher : mPushedMatchers) {
+ mProto.clear();
+ for (const auto& matcher : mSubscriptionInfo->mPushedMatchers) {
if (matchesSimple(*mUidMap, matcher, event)) {
- VLOG("%s", event.ToString().c_str());
uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
event.ToProto(mProto);
mProto.end(atomToken);
- // First write the payload size.
- size_t bufferSize = mProto.size();
- write(mOutput, &bufferSize, sizeof(bufferSize));
-
- // Then write the payload.
- mProto.flush(mOutput);
- mProto.clear();
- break;
+ attemptWriteToPipeLocked(mProto.size());
}
}
}
-void ShellSubscriber::binderDied(const wp<IBinder>& who) {
- {
- VLOG("Shell exits");
- std::lock_guard<std::mutex> lock(mMutex);
- cleanUpLocked();
+// Tries to write the atom encoded in mProto to the pipe. If the write fails
+// because the read end of the pipe has closed, signals to other threads that
+// the subscription should end.
+void ShellSubscriber::attemptWriteToPipeLocked(size_t dataSize) {
+ // First, write the payload size.
+ if (!android::base::WriteFully(mSubscriptionInfo->mOutputFd, &dataSize, sizeof(dataSize))) {
+ mSubscriptionInfo->mClientAlive = false;
+ mSubscriptionShouldEnd.notify_one();
+ return;
}
- mShellDied.notify_all();
+
+ // Then, write the payload if this is not just a heartbeat.
+ if (dataSize > 0 && !mProto.flush(mSubscriptionInfo->mOutputFd)) {
+ mSubscriptionInfo->mClientAlive = false;
+ mSubscriptionShouldEnd.notify_one();
+ return;
+ }
+
+ mLastWriteMs = getElapsedRealtimeMillis();
}
} // namespace statsd
diff --git a/cmds/statsd/src/shell/ShellSubscriber.h b/cmds/statsd/src/shell/ShellSubscriber.h
index 86d85901083a..4c05fa7f71c2 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.h
+++ b/cmds/statsd/src/shell/ShellSubscriber.h
@@ -16,16 +16,17 @@
#pragma once
-#include "logd/LogEvent.h"
-
#include <android/util/ProtoOutputStream.h>
-#include <binder/IResultReceiver.h>
+#include <private/android_filesystem_config.h>
+
#include <condition_variable>
#include <mutex>
#include <thread>
+
#include "external/StatsPullerManager.h"
#include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "logd/LogEvent.h"
#include "packages/UidMap.h"
namespace android {
@@ -37,11 +38,11 @@ namespace statsd {
*
* A shell subscription lasts *until shell exits*. Unlike config based clients, a shell client
* communicates with statsd via file descriptors. They can subscribe pushed and pulled atoms.
- * The atoms are sent back to the client in real time, as opposed to
- * keeping the data in memory. Shell clients do not subscribe aggregated metrics, as they are
- * responsible for doing the aggregation after receiving the atom events.
+ * The atoms are sent back to the client in real time, as opposed to keeping the data in memory.
+ * Shell clients do not subscribe aggregated metrics, as they are responsible for doing the
+ * aggregation after receiving the atom events.
*
- * Shell client pass ShellSubscription in the proto binary format. Client can update the
+ * Shell clients pass ShellSubscription in the proto binary format. Clients can update the
* subscription by sending a new subscription. The new subscription would replace the old one.
* Input data stream format is:
*
@@ -53,43 +54,70 @@ namespace statsd {
* The stream would be in the following format:
* |size_t|shellData proto|size_t|shellData proto|....
*
- * Only one shell subscriber allowed at a time, because each shell subscriber blocks one thread
+ * Only one shell subscriber is allowed at a time because each shell subscriber blocks one thread
* until it exits.
*/
-class ShellSubscriber : public virtual IBinder::DeathRecipient {
+class ShellSubscriber : public virtual RefBase {
public:
ShellSubscriber(sp<UidMap> uidMap, sp<StatsPullerManager> pullerMgr)
: mUidMap(uidMap), mPullerMgr(pullerMgr){};
- /**
- * Start a new subscription.
- */
- void startNewSubscription(int inFd, int outFd, sp<IResultReceiver> resultReceiver,
- int timeoutSec);
-
- void binderDied(const wp<IBinder>& who);
+ void startNewSubscription(int inFd, int outFd, int timeoutSec);
void onLogEvent(const LogEvent& event);
private:
struct PullInfo {
- PullInfo(const SimpleAtomMatcher& matcher, int64_t interval)
- : mPullerMatcher(matcher), mInterval(interval), mPrevPullElapsedRealtimeMs(0) {
+ PullInfo(const SimpleAtomMatcher& matcher, int64_t interval,
+ const std::vector<std::string>& packages, const std::vector<int32_t>& uids)
+ : mPullerMatcher(matcher),
+ mInterval(interval),
+ mPrevPullElapsedRealtimeMs(0),
+ mPullPackages(packages),
+ mPullUids(uids) {
}
SimpleAtomMatcher mPullerMatcher;
int64_t mInterval;
int64_t mPrevPullElapsedRealtimeMs;
+ std::vector<std::string> mPullPackages;
+ std::vector<int32_t> mPullUids;
+ };
+
+ struct SubscriptionInfo {
+ SubscriptionInfo(const int& inputFd, const int& outputFd)
+ : mInputFd(inputFd), mOutputFd(outputFd), mClientAlive(true) {
+ }
+
+ int mInputFd;
+ int mOutputFd;
+ std::vector<SimpleAtomMatcher> mPushedMatchers;
+ std::vector<PullInfo> mPulledInfo;
+ bool mClientAlive;
};
- void readConfig(int in);
- void updateConfig(const ShellSubscription& config);
+ int claimToken();
+
+ bool readConfig(std::shared_ptr<SubscriptionInfo> subscriptionInfo);
+
+ void spawnHelperThread(int myToken);
- void startPull(int64_t token, int64_t intervalMillis);
+ void waitForSubscriptionToEndLocked(std::shared_ptr<SubscriptionInfo> myInfo,
+ int myToken,
+ std::unique_lock<std::mutex>& lock,
+ int timeoutSec);
- void cleanUpLocked();
+ // Helper thread that pulls atoms at a regular frequency and sends
+ // heartbeats to perfd if statsd hasn't recently sent any data. Statsd must
+ // send heartbeats for perfd to escape a blocking read call and recheck if
+ // the user has terminated the subscription.
+ void pullAndSendHeartbeats(int myToken);
- void writeToOutputLocked(const vector<std::shared_ptr<LogEvent>>& data,
- const SimpleAtomMatcher& matcher);
+ void writePulledAtomsLocked(const vector<std::shared_ptr<LogEvent>>& data,
+ const SimpleAtomMatcher& matcher);
+
+ void getUidsForPullAtom(vector<int32_t>* uids, const PullInfo& pullInfo);
+
+ void attemptWriteToPipeLocked(size_t dataSize);
sp<UidMap> mUidMap;
@@ -99,19 +127,18 @@ private:
mutable std::mutex mMutex;
- std::condition_variable mShellDied; // semaphore for waiting until shell exits.
-
- int mInput; // The input file descriptor
-
- int mOutput; // The output file descriptor
+ std::condition_variable mSubscriptionShouldEnd;
- sp<IResultReceiver> mResultReceiver;
+ std::shared_ptr<SubscriptionInfo> mSubscriptionInfo = nullptr;
- std::vector<SimpleAtomMatcher> mPushedMatchers;
+ int mToken = 0;
- std::vector<PullInfo> mPulledInfo;
+ const int32_t DEFAULT_PULL_UID = AID_SYSTEM;
- int64_t mPullToken = 0; // A unique token to identify a puller thread.
+ // Tracks when we last send data to perfd. We need that time to determine
+ // when next to send a heartbeat.
+ int64_t mLastWriteMs = 0;
+ const int64_t kMsBetweenHeartbeats = 1000;
};
} // namespace statsd
diff --git a/cmds/statsd/src/shell/shell_config.proto b/cmds/statsd/src/shell/shell_config.proto
index 73cb49a61821..07d0310ef2dd 100644
--- a/cmds/statsd/src/shell/shell_config.proto
+++ b/cmds/statsd/src/shell/shell_config.proto
@@ -28,6 +28,9 @@ message PulledAtomSubscription {
/* gap between two pulls in milliseconds */
optional int32 freq_millis = 2;
+
+ /* Packages that the pull is requested from */
+ repeated string packages = 3;
}
message ShellSubscription {
diff --git a/cmds/statsd/src/socket/StatsSocketListener.cpp b/cmds/statsd/src/socket/StatsSocketListener.cpp
index bedfa7dec7a4..b877cc9c352f 100755
--- a/cmds/statsd/src/socket/StatsSocketListener.cpp
+++ b/cmds/statsd/src/socket/StatsSocketListener.cpp
@@ -36,8 +36,6 @@ namespace android {
namespace os {
namespace statsd {
-static const int kLogMsgHeaderSize = 28;
-
StatsSocketListener::StatsSocketListener(std::shared_ptr<LogEventQueue> queue)
: SocketListener(getLogSocket(), false /*start listen*/), mQueue(queue) {
}
@@ -92,7 +90,7 @@ bool StatsSocketListener::onDataAvailable(SocketClient* cli) {
cred->uid = DEFAULT_OVERFLOWUID;
}
- char* ptr = ((char*)buffer) + sizeof(android_log_header_t);
+ uint8_t* ptr = ((uint8_t*)buffer) + sizeof(android_log_header_t);
n -= sizeof(android_log_header_t);
// When a log failed to write to statsd socket (e.g., due ot EBUSY), a special message would
@@ -121,18 +119,17 @@ bool StatsSocketListener::onDataAvailable(SocketClient* cli) {
}
}
- log_msg msg;
-
- msg.entry.len = n;
- msg.entry.hdr_size = kLogMsgHeaderSize;
- msg.entry.sec = time(nullptr);
- msg.entry.pid = cred->pid;
- msg.entry.uid = cred->uid;
-
- memcpy(msg.buf + kLogMsgHeaderSize, ptr, n + 1);
+ // move past the 4-byte StatsEventTag
+ uint8_t* msg = ptr + sizeof(uint32_t);
+ uint32_t len = n - sizeof(uint32_t);
+ uint32_t uid = cred->uid;
+ uint32_t pid = cred->pid;
int64_t oldestTimestamp;
- if (!mQueue->push(std::make_unique<LogEvent>(msg), &oldestTimestamp)) {
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(uid, pid);
+ logEvent->parseBuffer(msg, len);
+
+ if (!mQueue->push(std::move(logEvent), &oldestTimestamp)) {
StatsdStats::getInstance().noteEventQueueOverflow(oldestTimestamp);
}
diff --git a/cmds/statsd/src/state/StateListener.h b/cmds/statsd/src/state/StateListener.h
new file mode 100644
index 000000000000..63880017ca18
--- /dev/null
+++ b/cmds/statsd/src/state/StateListener.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019, 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 <utils/RefBase.h>
+
+#include "HashableDimensionKey.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class StateListener : public virtual RefBase {
+public:
+ StateListener(){};
+
+ virtual ~StateListener(){};
+
+ /**
+ * Interface for handling a state change.
+ *
+ * The old and new state values map to the original state values.
+ * StateTrackers only track the original state values and are unaware
+ * of higher-level state groups. MetricProducers hold information on
+ * state groups and are responsible for mapping original state values to
+ * the correct state group.
+ *
+ * [eventTimeNs]: Time of the state change log event.
+ * [atomId]: The id of the state atom
+ * [primaryKey]: The primary field values of the state atom
+ * [oldState]: Previous state value before state change
+ * [newState]: Current state value after state change
+ */
+ virtual void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
+ const HashableDimensionKey& primaryKey, const FieldValue& oldState,
+ const FieldValue& newState) = 0;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/state/StateManager.cpp b/cmds/statsd/src/state/StateManager.cpp
new file mode 100644
index 000000000000..c29afeb794fa
--- /dev/null
+++ b/cmds/statsd/src/state/StateManager.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2019, 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.
+ */
+
+#define DEBUG false // STOPSHIP if true
+#include "Log.h"
+
+#include "StateManager.h"
+
+#include <private/android_filesystem_config.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+StateManager::StateManager()
+ : mAllowedPkg({
+ "com.android.systemui",
+ }) {
+}
+
+StateManager& StateManager::getInstance() {
+ static StateManager sStateManager;
+ return sStateManager;
+}
+
+void StateManager::clear() {
+ mStateTrackers.clear();
+}
+
+void StateManager::onLogEvent(const LogEvent& event) {
+ // Only process state events from uids in AID_* and packages that are whitelisted in
+ // mAllowedPkg.
+ // Whitelisted AIDs are AID_ROOT and all AIDs in [1000, 2000)
+ if (event.GetUid() == AID_ROOT || (event.GetUid() >= 1000 && event.GetUid() < 2000) ||
+ mAllowedLogSources.find(event.GetUid()) != mAllowedLogSources.end()) {
+ if (mStateTrackers.find(event.GetTagId()) != mStateTrackers.end()) {
+ mStateTrackers[event.GetTagId()]->onLogEvent(event);
+ }
+ }
+}
+
+void StateManager::registerListener(const int32_t atomId, wp<StateListener> listener) {
+ // Check if state tracker already exists.
+ if (mStateTrackers.find(atomId) == mStateTrackers.end()) {
+ mStateTrackers[atomId] = new StateTracker(atomId);
+ }
+ mStateTrackers[atomId]->registerListener(listener);
+}
+
+void StateManager::unregisterListener(const int32_t atomId, wp<StateListener> listener) {
+ std::unique_lock<std::mutex> lock(mMutex);
+
+ // Hold the sp<> until the lock is released so that ~StateTracker() is
+ // not called while the lock is held.
+ sp<StateTracker> toRemove;
+
+ // Unregister listener from correct StateTracker
+ auto it = mStateTrackers.find(atomId);
+ if (it != mStateTrackers.end()) {
+ it->second->unregisterListener(listener);
+
+ // Remove the StateTracker if it has no listeners
+ if (it->second->getListenersCount() == 0) {
+ toRemove = it->second;
+ mStateTrackers.erase(it);
+ }
+ } else {
+ ALOGE("StateManager cannot unregister listener, StateTracker for atom %d does not exist",
+ atomId);
+ }
+ lock.unlock();
+}
+
+bool StateManager::getStateValue(const int32_t atomId, const HashableDimensionKey& key,
+ FieldValue* output) const {
+ auto it = mStateTrackers.find(atomId);
+ if (it != mStateTrackers.end()) {
+ return it->second->getStateValue(key, output);
+ }
+ return false;
+}
+
+void StateManager::updateLogSources(const sp<UidMap>& uidMap) {
+ mAllowedLogSources.clear();
+ for (const auto& pkg : mAllowedPkg) {
+ auto uids = uidMap->getAppUid(pkg);
+ mAllowedLogSources.insert(uids.begin(), uids.end());
+ }
+}
+
+void StateManager::notifyAppChanged(const string& apk, const sp<UidMap>& uidMap) {
+ if (mAllowedPkg.find(apk) != mAllowedPkg.end()) {
+ updateLogSources(uidMap);
+ }
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/state/StateManager.h b/cmds/statsd/src/state/StateManager.h
new file mode 100644
index 000000000000..18c404c29c4e
--- /dev/null
+++ b/cmds/statsd/src/state/StateManager.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2019, 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 <inttypes.h>
+#include <utils/RefBase.h>
+
+#include <set>
+#include <string>
+#include <unordered_map>
+
+#include "HashableDimensionKey.h"
+#include "packages/UidMap.h"
+#include "state/StateListener.h"
+#include "state/StateTracker.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * This class is NOT thread safe.
+ * It should only be used while StatsLogProcessor's lock is held.
+ */
+class StateManager : public virtual RefBase {
+public:
+ StateManager();
+
+ ~StateManager(){};
+
+ // Returns a pointer to the single, shared StateManager object.
+ static StateManager& getInstance();
+
+ // Unregisters all listeners and removes all trackers from StateManager.
+ void clear();
+
+ // Notifies the correct StateTracker of an event.
+ void onLogEvent(const LogEvent& event);
+
+ // Notifies the StateTracker for the given atomId to register listener.
+ // If the correct StateTracker does not exist, a new StateTracker is created.
+ // Note: StateTrackers can be created for non-state atoms. They are essentially empty and
+ // do not perform any actions.
+ void registerListener(const int32_t atomId, wp<StateListener> listener);
+
+ // Notifies the correct StateTracker to unregister a listener
+ // and removes the tracker if it no longer has any listeners.
+ void unregisterListener(const int32_t atomId, wp<StateListener> listener);
+
+ // Returns true if the StateTracker exists and queries for the
+ // original state value mapped to the given query key. The state value is
+ // stored and output in a FieldValue class.
+ // Returns false if the StateTracker doesn't exist.
+ bool getStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
+ FieldValue* output) const;
+
+ // Updates mAllowedLogSources with the latest uids for the packages that are allowed to log.
+ void updateLogSources(const sp<UidMap>& uidMap);
+
+ void notifyAppChanged(const string& apk, const sp<UidMap>& uidMap);
+
+ inline int getStateTrackersCount() const {
+ return mStateTrackers.size();
+ }
+
+ inline int getListenersCount(const int32_t atomId) const {
+ auto it = mStateTrackers.find(atomId);
+ if (it != mStateTrackers.end()) {
+ return it->second->getListenersCount();
+ }
+ return -1;
+ }
+
+private:
+ mutable std::mutex mMutex;
+
+ // Maps state atom ids to StateTrackers
+ std::unordered_map<int32_t, sp<StateTracker>> mStateTrackers;
+
+ // The package names that can log state events.
+ const std::set<std::string> mAllowedPkg;
+
+ // The combined uid sources (after translating pkg name to uid).
+ // State events from uids that are not in the list will be ignored to avoid state pollution.
+ std::set<int32_t> mAllowedLogSources;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp
new file mode 100644
index 000000000000..41e525c343ba
--- /dev/null
+++ b/cmds/statsd/src/state/StateTracker.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2019, 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.
+ */
+
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include "stats_util.h"
+
+#include "StateTracker.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+StateTracker::StateTracker(const int32_t atomId) : mField(atomId, 0) {
+}
+
+void StateTracker::onLogEvent(const LogEvent& event) {
+ const int64_t eventTimeNs = event.GetElapsedTimestampNs();
+
+ // Parse event for primary field values i.e. primary key.
+ HashableDimensionKey primaryKey;
+ filterPrimaryKey(event.getValues(), &primaryKey);
+
+ FieldValue newState;
+ if (!getStateFieldValueFromLogEvent(event, &newState)) {
+ ALOGE("StateTracker error extracting state from log event. Missing exclusive state field.");
+ clearStateForPrimaryKey(eventTimeNs, primaryKey);
+ return;
+ }
+
+ mField.setField(newState.mField.getField());
+
+ if (newState.mValue.getType() != INT) {
+ ALOGE("StateTracker error extracting state from log event. Type: %d",
+ newState.mValue.getType());
+ clearStateForPrimaryKey(eventTimeNs, primaryKey);
+ return;
+ }
+
+ if (int resetState = event.getResetState(); resetState != -1) {
+ VLOG("StateTracker new reset state: %d", resetState);
+ const FieldValue resetStateFieldValue(mField, Value(resetState));
+ handleReset(eventTimeNs, resetStateFieldValue);
+ return;
+ }
+
+ const bool nested = newState.mAnnotations.isNested();
+ StateValueInfo* stateValueInfo = &mStateMap[primaryKey];
+ updateStateForPrimaryKey(eventTimeNs, primaryKey, newState, nested, stateValueInfo);
+}
+
+void StateTracker::registerListener(wp<StateListener> listener) {
+ mListeners.insert(listener);
+}
+
+void StateTracker::unregisterListener(wp<StateListener> listener) {
+ mListeners.erase(listener);
+}
+
+bool StateTracker::getStateValue(const HashableDimensionKey& queryKey, FieldValue* output) const {
+ output->mField = mField;
+
+ if (const auto it = mStateMap.find(queryKey); it != mStateMap.end()) {
+ output->mValue = it->second.state;
+ return true;
+ }
+
+ // Set the state value to kStateUnknown if query key is not found in state map.
+ output->mValue = kStateUnknown;
+ return false;
+}
+
+void StateTracker::handleReset(const int64_t eventTimeNs, const FieldValue& newState) {
+ VLOG("StateTracker handle reset");
+ for (auto& [primaryKey, stateValueInfo] : mStateMap) {
+ updateStateForPrimaryKey(eventTimeNs, primaryKey, newState,
+ false /* nested; treat this state change as not nested */,
+ &stateValueInfo);
+ }
+}
+
+void StateTracker::clearStateForPrimaryKey(const int64_t eventTimeNs,
+ const HashableDimensionKey& primaryKey) {
+ VLOG("StateTracker clear state for primary key");
+ const std::unordered_map<HashableDimensionKey, StateValueInfo>::iterator it =
+ mStateMap.find(primaryKey);
+
+ // If there is no entry for the primaryKey in mStateMap, then the state is already
+ // kStateUnknown.
+ const FieldValue state(mField, Value(kStateUnknown));
+ if (it != mStateMap.end()) {
+ updateStateForPrimaryKey(eventTimeNs, primaryKey, state,
+ false /* nested; treat this state change as not nested */,
+ &it->second);
+ }
+}
+
+void StateTracker::updateStateForPrimaryKey(const int64_t eventTimeNs,
+ const HashableDimensionKey& primaryKey,
+ const FieldValue& newState, const bool nested,
+ StateValueInfo* stateValueInfo) {
+ FieldValue oldState;
+ oldState.mField = mField;
+ oldState.mValue.setInt(stateValueInfo->state);
+ const int32_t oldStateValue = stateValueInfo->state;
+ const int32_t newStateValue = newState.mValue.int_value;
+
+ if (kStateUnknown == newStateValue) {
+ mStateMap.erase(primaryKey);
+ }
+
+ // Update state map for non-nested counting case.
+ // Every state event triggers a state overwrite.
+ if (!nested) {
+ stateValueInfo->state = newStateValue;
+ stateValueInfo->count = 1;
+
+ // Notify listeners if state has changed.
+ if (oldStateValue != newStateValue) {
+ notifyListeners(eventTimeNs, primaryKey, oldState, newState);
+ }
+ return;
+ }
+
+ // Update state map for nested counting case.
+ //
+ // Nested counting is only allowed for binary state events such as ON/OFF or
+ // ACQUIRE/RELEASE. For example, WakelockStateChanged might have the state
+ // events: ON, ON, OFF. The state will still be ON until we see the same
+ // number of OFF events as ON events.
+ //
+ // In atoms.proto, a state atom with nested counting enabled
+ // must only have 2 states. There is no enforcemnt here of this requirement.
+ // The atom must be logged correctly.
+ if (kStateUnknown == newStateValue) {
+ if (kStateUnknown != oldStateValue) {
+ notifyListeners(eventTimeNs, primaryKey, oldState, newState);
+ }
+ } else if (oldStateValue == kStateUnknown) {
+ stateValueInfo->state = newStateValue;
+ stateValueInfo->count = 1;
+ notifyListeners(eventTimeNs, primaryKey, oldState, newState);
+ } else if (oldStateValue == newStateValue) {
+ stateValueInfo->count++;
+ } else if (--stateValueInfo->count == 0) {
+ stateValueInfo->state = newStateValue;
+ stateValueInfo->count = 1;
+ notifyListeners(eventTimeNs, primaryKey, oldState, newState);
+ }
+}
+
+void StateTracker::notifyListeners(const int64_t eventTimeNs,
+ const HashableDimensionKey& primaryKey,
+ const FieldValue& oldState, const FieldValue& newState) {
+ for (auto l : mListeners) {
+ auto sl = l.promote();
+ if (sl != nullptr) {
+ sl->onStateChanged(eventTimeNs, mField.getTag(), primaryKey, oldState, newState);
+ }
+ }
+}
+
+bool getStateFieldValueFromLogEvent(const LogEvent& event, FieldValue* output) {
+ const int exclusiveStateFieldIndex = event.getExclusiveStateFieldIndex();
+ if (-1 == exclusiveStateFieldIndex) {
+ ALOGE("error extracting state from log event. Missing exclusive state field.");
+ return false;
+ }
+
+ *output = event.getValues()[exclusiveStateFieldIndex];
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/state/StateTracker.h b/cmds/statsd/src/state/StateTracker.h
new file mode 100644
index 000000000000..abd579e7e302
--- /dev/null
+++ b/cmds/statsd/src/state/StateTracker.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2019, 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 <utils/RefBase.h>
+#include "HashableDimensionKey.h"
+#include "logd/LogEvent.h"
+
+#include "state/StateListener.h"
+
+#include <unordered_map>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class StateTracker : public virtual RefBase {
+public:
+ StateTracker(const int32_t atomId);
+
+ virtual ~StateTracker(){};
+
+ // Updates state map and notifies all listeners if a state change occurs.
+ // Checks if a state change has occurred by getting the state value from
+ // the log event and comparing the old and new states.
+ void onLogEvent(const LogEvent& event);
+
+ // Adds new listeners to set of StateListeners. If a listener is already
+ // registered, it is ignored.
+ void registerListener(wp<StateListener> listener);
+
+ void unregisterListener(wp<StateListener> listener);
+
+ // The output is a FieldValue object that has mStateField as the field and
+ // the original state value (found using the given query key) as the value.
+ //
+ // If the key isn't mapped to a state or the key size doesn't match the
+ // number of primary fields, the output value is set to kStateUnknown.
+ bool getStateValue(const HashableDimensionKey& queryKey, FieldValue* output) const;
+
+ inline int getListenersCount() const {
+ return mListeners.size();
+ }
+
+ const static int kStateUnknown = -1;
+
+private:
+ struct StateValueInfo {
+ int32_t state = kStateUnknown; // state value
+ int count = 0; // nested count (only used for binary states)
+ };
+
+ Field mField;
+
+ // Maps primary key to state value info
+ std::unordered_map<HashableDimensionKey, StateValueInfo> mStateMap;
+
+ // Set of all StateListeners (objects listening for state changes)
+ std::set<wp<StateListener>> mListeners;
+
+ // Reset all state values in map to the given state.
+ void handleReset(const int64_t eventTimeNs, const FieldValue& newState);
+
+ // Clears the state value mapped to the given primary key by setting it to kStateUnknown.
+ void clearStateForPrimaryKey(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey);
+
+ // Update the StateMap based on the received state value.
+ void updateStateForPrimaryKey(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey,
+ const FieldValue& newState, const bool nested,
+ StateValueInfo* stateValueInfo);
+
+ // Notify registered state listeners of state change.
+ void notifyListeners(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey,
+ const FieldValue& oldState, const FieldValue& newState);
+};
+
+bool getStateFieldValueFromLogEvent(const LogEvent& event, FieldValue* output);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index b87547056ad3..ddd2725c9cb9 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -41,6 +41,15 @@ message DimensionsValueTuple {
repeated DimensionsValue dimensions_value = 1;
}
+message StateValue {
+ optional int32 atom_id = 1;
+
+ oneof contents {
+ int64 group_id = 2;
+ int32 value = 3;
+ }
+}
+
message EventMetricData {
optional int64 elapsed_timestamp_nanos = 1;
@@ -66,13 +75,15 @@ message CountBucketInfo {
message CountMetricData {
optional DimensionsValue dimensions_in_what = 1;
- optional DimensionsValue dimensions_in_condition = 2;
+ repeated StateValue slice_by_state = 6;
repeated CountBucketInfo bucket_info = 3;
repeated DimensionsValue dimension_leaf_values_in_what = 4;
- repeated DimensionsValue dimension_leaf_values_in_condition = 5;
+ optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
+
+ repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
}
message DurationBucketInfo {
@@ -92,13 +103,15 @@ message DurationBucketInfo {
message DurationMetricData {
optional DimensionsValue dimensions_in_what = 1;
- optional DimensionsValue dimensions_in_condition = 2;
+ repeated StateValue slice_by_state = 6;
repeated DurationBucketInfo bucket_info = 3;
repeated DimensionsValue dimension_leaf_values_in_what = 4;
- repeated DimensionsValue dimension_leaf_values_in_condition = 5;
+ optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
+
+ repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
}
message ValueBucketInfo {
@@ -136,13 +149,15 @@ message ValueBucketInfo {
message ValueMetricData {
optional DimensionsValue dimensions_in_what = 1;
- optional DimensionsValue dimensions_in_condition = 2;
+ repeated StateValue slice_by_state = 6;
repeated ValueBucketInfo bucket_info = 3;
repeated DimensionsValue dimension_leaf_values_in_what = 4;
- repeated DimensionsValue dimension_leaf_values_in_condition = 5;
+ optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
+
+ repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
}
message GaugeBucketInfo {
@@ -166,13 +181,16 @@ message GaugeBucketInfo {
message GaugeMetricData {
optional DimensionsValue dimensions_in_what = 1;
- optional DimensionsValue dimensions_in_condition = 2;
+ // Currently unsupported
+ repeated StateValue slice_by_state = 6;
repeated GaugeBucketInfo bucket_info = 3;
repeated DimensionsValue dimension_leaf_values_in_what = 4;
- repeated DimensionsValue dimension_leaf_values_in_condition = 5;
+ optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
+
+ repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
}
message StatsLogReport {
@@ -180,11 +198,42 @@ message StatsLogReport {
// Fields 2 and 3 are reserved.
+ // Keep this in sync with BucketDropReason enum in MetricProducer.h.
+ enum BucketDropReason {
+ // For ValueMetric, a bucket is dropped during a dump report request iff
+ // current bucket should be included, a pull is needed (pulled metric and
+ // condition is true), and we are under fast time constraints.
+ DUMP_REPORT_REQUESTED = 1;
+ EVENT_IN_WRONG_BUCKET = 2;
+ CONDITION_UNKNOWN = 3;
+ PULL_FAILED = 4;
+ PULL_DELAYED = 5;
+ DIMENSION_GUARDRAIL_REACHED = 6;
+ MULTIPLE_BUCKETS_SKIPPED = 7;
+ // Not an invalid bucket case, but the bucket is dropped.
+ BUCKET_TOO_SMALL = 8;
+ // Not an invalid bucket case, but the bucket is skipped.
+ NO_DATA = 9;
+ };
+
+ message DropEvent {
+ optional BucketDropReason drop_reason = 1;
+
+ optional int64 drop_time_millis = 2;
+ }
+
message SkippedBuckets {
optional int64 start_bucket_elapsed_nanos = 1;
+
optional int64 end_bucket_elapsed_nanos = 2;
+
optional int64 start_bucket_elapsed_millis = 3;
+
optional int64 end_bucket_elapsed_millis = 4;
+
+ // The number of drop events is capped by StatsdStats::kMaxLoggedBucketDropEvents.
+ // The current maximum is 10 drop events.
+ repeated DropEvent drop_event = 5;
}
message EventMetricDataWrapper {
@@ -220,7 +269,7 @@ message StatsLogReport {
optional DimensionsValue dimensions_path_in_what = 11;
- optional DimensionsValue dimensions_path_in_condition = 12;
+ optional DimensionsValue dimensions_path_in_condition = 12 [deprecated = true];
// DO NOT USE field 13.
@@ -363,7 +412,7 @@ message StatsdStatsReport {
repeated ConditionStats condition_stats = 14;
repeated MetricStats metric_stats = 15;
repeated AlertStats alert_stats = 16;
- repeated MetricStats metric_dimension_in_condition_stats = 17;
+ repeated MetricStats metric_dimension_in_condition_stats = 17 [deprecated = true];
message Annotation {
optional int64 field_int64 = 1;
optional int32 field_int32 = 2;
@@ -378,6 +427,7 @@ message StatsdStatsReport {
message AtomStats {
optional int32 tag = 1;
optional int32 count = 2;
+ optional int32 error_count = 3;
}
repeated AtomStats atom_stats = 7;
@@ -408,11 +458,20 @@ message StatsdStatsReport {
optional int64 pull_timeout = 10;
optional int64 pull_exceed_max_delay = 11;
optional int64 pull_failed = 12;
- optional int64 stats_companion_pull_failed = 13;
- optional int64 stats_companion_pull_binder_transaction_failed = 14;
+ optional int64 stats_companion_pull_failed = 13 [deprecated = true];
+ optional int64 stats_companion_pull_binder_transaction_failed = 14 [deprecated = true];
optional int64 empty_data = 15;
optional int64 registered_count = 16;
optional int64 unregistered_count = 17;
+ optional int32 atom_error_count = 18;
+ optional int64 binder_call_failed = 19;
+ optional int64 failed_uid_provider_not_found = 20;
+ optional int64 puller_not_found = 21;
+ message PullTimeoutMetadata {
+ optional int64 pull_timeout_uptime_millis = 1;
+ optional int64 pull_timeout_elapsed_millis = 2;
+ }
+ repeated PullTimeoutMetadata pull_atom_metadata = 22;
}
repeated PulledAtomStats pulled_atom_stats = 10;
@@ -483,7 +542,7 @@ message AlertTriggerDetails {
message MetricValue {
optional int64 metric_id = 1;
optional DimensionsValue dimension_in_what = 2;
- optional DimensionsValue dimension_in_condition = 3;
+ optional DimensionsValue dimension_in_condition = 3 [deprecated = true];
optional int64 value = 4;
}
oneof value {
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index a7b681014292..423bae8bc0a4 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -17,11 +17,13 @@
#include "hash.h"
#include "stats_log_util.h"
+#include <aidl/android/os/IStatsCompanionService.h>
#include <private/android_filesystem_config.h>
#include <set>
#include <utils/SystemClock.h>
-using android::util::AtomsInfo;
+#include "statscompanion_util.h"
+
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_FIXED64;
@@ -33,6 +35,10 @@ using android::util::FIELD_TYPE_STRING;
using android::util::FIELD_TYPE_UINT64;
using android::util::ProtoOutputStream;
+using aidl::android::os::IStatsCompanionService;
+using std::shared_ptr;
+using std::string;
+
namespace android {
namespace os {
namespace statsd {
@@ -49,6 +55,11 @@ const int DIMENSIONS_VALUE_VALUE_STR_HASH = 8;
const int DIMENSIONS_VALUE_TUPLE_VALUE = 1;
+// for StateValue Proto
+const int STATE_VALUE_ATOM_ID = 1;
+const int STATE_VALUE_CONTENTS_GROUP_ID = 2;
+const int STATE_VALUE_CONTENTS_VALUE = 3;
+
// for PulledAtomStats proto
const int FIELD_ID_PULLED_ATOM_STATS = 10;
const int FIELD_ID_PULL_ATOM_ID = 1;
@@ -63,11 +74,17 @@ const int FIELD_ID_DATA_ERROR = 9;
const int FIELD_ID_PULL_TIMEOUT = 10;
const int FIELD_ID_PULL_EXCEED_MAX_DELAY = 11;
const int FIELD_ID_PULL_FAILED = 12;
-const int FIELD_ID_STATS_COMPANION_FAILED = 13;
-const int FIELD_ID_STATS_COMPANION_BINDER_TRANSACTION_FAILED = 14;
const int FIELD_ID_EMPTY_DATA = 15;
const int FIELD_ID_PULL_REGISTERED_COUNT = 16;
const int FIELD_ID_PULL_UNREGISTERED_COUNT = 17;
+const int FIELD_ID_ATOM_ERROR_COUNT = 18;
+const int FIELD_ID_BINDER_CALL_FAIL_COUNT = 19;
+const int FIELD_ID_PULL_UID_PROVIDER_NOT_FOUND = 20;
+const int FIELD_ID_PULLER_NOT_FOUND = 21;
+const int FIELD_ID_PULL_TIMEOUT_METADATA = 22;
+const int FIELD_ID_PULL_TIMEOUT_METADATA_UPTIME_MILLIS = 1;
+const int FIELD_ID_PULL_TIMEOUT_METADATA_ELAPSED_MILLIS = 2;
+
// for AtomMetricStats proto
const int FIELD_ID_ATOM_METRIC_STATS = 17;
const int FIELD_ID_METRIC_ID = 1;
@@ -345,30 +362,7 @@ void writeFieldValueTreeToStreamHelper(int tagId, const std::vector<FieldValue>&
protoOutput->write(FIELD_TYPE_FLOAT | fieldNum, dim.mValue.float_value);
break;
case STRING: {
- bool isBytesField = false;
- // Bytes field is logged via string format in log_msg format. So here we check
- // if this string field is a byte field.
- std::map<int, std::vector<int>>::const_iterator itr;
- if (depth == 0 && (itr = AtomsInfo::kBytesFieldAtoms.find(tagId)) !=
- AtomsInfo::kBytesFieldAtoms.end()) {
- const std::vector<int>& bytesFields = itr->second;
- for (int bytesField : bytesFields) {
- if (bytesField == fieldNum) {
- // This is a bytes field
- isBytesField = true;
- break;
- }
- }
- }
- if (isBytesField) {
- if (dim.mValue.str_value.length() > 0) {
- protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum,
- (const char*)dim.mValue.str_value.c_str(),
- dim.mValue.str_value.length());
- }
- } else {
- protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value);
- }
+ protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value);
break;
}
case STORAGE:
@@ -412,6 +406,23 @@ void writeFieldValueTreeToStream(int tagId, const std::vector<FieldValue>& value
protoOutput->end(atomToken);
}
+void writeStateToProto(const FieldValue& state, util::ProtoOutputStream* protoOutput) {
+ protoOutput->write(FIELD_TYPE_INT32 | STATE_VALUE_ATOM_ID, state.mField.getTag());
+
+ switch (state.mValue.getType()) {
+ case INT:
+ protoOutput->write(FIELD_TYPE_INT32 | STATE_VALUE_CONTENTS_VALUE,
+ state.mValue.int_value);
+ break;
+ case LONG:
+ protoOutput->write(FIELD_TYPE_INT64 | STATE_VALUE_CONTENTS_GROUP_ID,
+ state.mValue.long_value);
+ break;
+ default:
+ break;
+ }
+}
+
int64_t TimeUnitToBucketSizeInMillisGuardrailed(int uid, TimeUnit unit) {
int64_t bucketSizeMillis = TimeUnitToBucketSizeInMillis(unit);
if (bucketSizeMillis > 1000 && bucketSizeMillis < 5 * 60 * 1000LL && uid != AID_SHELL &&
@@ -441,6 +452,8 @@ int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit) {
return 12 * 60 * 60 * 1000LL;
case ONE_DAY:
return 24 * 60 * 60 * 1000LL;
+ case ONE_WEEK:
+ return 7 * 24 * 60 * 60 * 1000LL;
case CTS:
return 1000;
case TIME_UNIT_UNSPECIFIED:
@@ -474,16 +487,29 @@ void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats>
(long long)pair.second.pullExceedMaxDelay);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_FAILED,
(long long)pair.second.pullFailed);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_STATS_COMPANION_FAILED,
- (long long)pair.second.statsCompanionPullFailed);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_STATS_COMPANION_BINDER_TRANSACTION_FAILED,
- (long long)pair.second.statsCompanionPullBinderTransactionFailed);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_EMPTY_DATA,
(long long)pair.second.emptyData);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_REGISTERED_COUNT,
(long long) pair.second.registeredCount);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_UNREGISTERED_COUNT,
(long long) pair.second.unregisteredCount);
+ protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_ERROR_COUNT, pair.second.atomErrorCount);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BINDER_CALL_FAIL_COUNT,
+ (long long)pair.second.binderCallFailCount);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_UID_PROVIDER_NOT_FOUND,
+ (long long)pair.second.pullUidProviderNotFound);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULLER_NOT_FOUND,
+ (long long)pair.second.pullerNotFound);
+ for (const auto& pullTimeoutMetadata : pair.second.pullTimeoutMetadata) {
+ uint64_t timeoutMetadataToken = protoOutput->start(FIELD_TYPE_MESSAGE |
+ FIELD_ID_PULL_TIMEOUT_METADATA |
+ FIELD_COUNT_REPEATED);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_TIMEOUT_METADATA_UPTIME_MILLIS,
+ pullTimeoutMetadata.pullTimeoutUptimeMillis);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_TIMEOUT_METADATA_ELAPSED_MILLIS,
+ pullTimeoutMetadata.pullTimeoutElapsedMillis);
+ protoOutput->end(timeoutMetadataToken);
+ }
protoOutput->end(token);
}
@@ -529,6 +555,10 @@ int64_t getElapsedRealtimeMillis() {
return ::android::elapsedRealtime();
}
+int64_t getSystemUptimeMillis() {
+ return ::android::uptimeMillis();
+}
+
int64_t getWallClockNs() {
return time(nullptr) * NS_PER_SEC;
}
@@ -541,14 +571,13 @@ int64_t getWallClockMillis() {
return time(nullptr) * MS_PER_SEC;
}
-int64_t truncateTimestampIfNecessary(int atomId, int64_t timestampNs) {
- if (AtomsInfo::kTruncatingTimestampAtomBlackList.find(atomId) !=
- AtomsInfo::kTruncatingTimestampAtomBlackList.end() ||
- (atomId >= StatsdStats::kTimestampTruncationStartTag &&
- atomId <= StatsdStats::kTimestampTruncationEndTag)) {
- return timestampNs / NS_PER_SEC / (5 * 60) * NS_PER_SEC * (5 * 60);
+int64_t truncateTimestampIfNecessary(const LogEvent& event) {
+ if (event.shouldTruncateTimestamp() ||
+ (event.GetTagId() >= StatsdStats::kTimestampTruncationStartTag &&
+ event.GetTagId() <= StatsdStats::kTimestampTruncationEndTag)) {
+ return event.GetElapsedTimestampNs() / NS_PER_SEC / (5 * 60) * NS_PER_SEC * (5 * 60);
} else {
- return timestampNs;
+ return event.GetElapsedTimestampNs();
}
}
@@ -560,6 +589,21 @@ int64_t MillisToNano(const int64_t millis) {
return millis * 1000000;
}
+bool checkPermissionForIds(const char* permission, pid_t pid, uid_t uid) {
+ shared_ptr<IStatsCompanionService> scs = getStatsCompanionService();
+ if (scs == nullptr) {
+ return false;
+ }
+
+ bool success;
+ ::ndk::ScopedAStatus status = scs->checkPermission(string(permission), pid, uid, &success);
+ if (!status.isOk()) {
+ return false;
+ }
+
+ return success;
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h
index a28165d09cdf..eb65dc6979c5 100644
--- a/cmds/statsd/src/stats_log_util.h
+++ b/cmds/statsd/src/stats_log_util.h
@@ -17,28 +17,33 @@
#pragma once
#include <android/util/ProtoOutputStream.h>
+
#include "FieldValue.h"
#include "HashableDimensionKey.h"
-#include "atoms_info.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "guardrail/StatsdStats.h"
+#include "logd/LogEvent.h"
+
+using android::util::ProtoOutputStream;
namespace android {
namespace os {
namespace statsd {
void writeFieldValueTreeToStream(int tagId, const std::vector<FieldValue>& values,
- util::ProtoOutputStream* protoOutput);
+ ProtoOutputStream* protoOutput);
void writeDimensionToProto(const HashableDimensionKey& dimension, std::set<string> *str_set,
- util::ProtoOutputStream* protoOutput);
+ ProtoOutputStream* protoOutput);
void writeDimensionLeafNodesToProto(const HashableDimensionKey& dimension,
const int dimensionLeafFieldId,
std::set<string> *str_set,
- util::ProtoOutputStream* protoOutput);
+ ProtoOutputStream* protoOutput);
void writeDimensionPathToProto(const std::vector<Matcher>& fieldMatchers,
- util::ProtoOutputStream* protoOutput);
+ ProtoOutputStream* protoOutput);
+
+void writeStateToProto(const FieldValue& state, ProtoOutputStream* protoOutput);
// Convert the TimeUnit enum to the bucket size in millis with a guardrail on
// bucket size.
@@ -56,6 +61,9 @@ int64_t getElapsedRealtimeMillis();
// Gets the elapsed timestamp in seconds.
int64_t getElapsedRealtimeSec();
+// Gets the system uptime in millis.
+int64_t getSystemUptimeMillis();
+
// Gets the wall clock timestamp in ns.
int64_t getWallClockNs();
@@ -71,14 +79,14 @@ int64_t MillisToNano(const int64_t millis);
// Helper function to write PulledAtomStats to ProtoOutputStream
void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats>& pair,
- util::ProtoOutputStream* protoOutput);
+ ProtoOutputStream* protoOutput);
// Helper function to write AtomMetricStats to ProtoOutputStream
void writeAtomMetricStatsToStream(const std::pair<int64_t, StatsdStats::AtomMetricStats> &pair,
- util::ProtoOutputStream *protoOutput);
+ ProtoOutputStream *protoOutput);
template<class T>
-bool parseProtoOutputStream(util::ProtoOutputStream& protoOutput, T* message) {
+bool parseProtoOutputStream(ProtoOutputStream& protoOutput, T* message) {
std::string pbBytes;
sp<android::util::ProtoReader> reader = protoOutput.data();
while (reader->readBuffer() != NULL) {
@@ -89,14 +97,21 @@ bool parseProtoOutputStream(util::ProtoOutputStream& protoOutput, T* message) {
return message->ParseFromArray(pbBytes.c_str(), pbBytes.size());
}
-// Checks the blacklist of atoms as well as the blacklisted range of 300,000 - 304,999.
+// Checks the truncate timestamp annotation as well as the blacklisted range of 300,000 - 304,999.
// Returns the truncated timestamp to the nearest 5 minutes if needed.
-int64_t truncateTimestampIfNecessary(int atomId, int64_t timestampNs);
+int64_t truncateTimestampIfNecessary(const LogEvent& event);
+
+// Checks permission for given pid and uid.
+bool checkPermissionForIds(const char* permission, pid_t pid, uid_t uid);
inline bool isVendorPulledAtom(int atomId) {
return atomId >= StatsdStats::kVendorPulledAtomStartTag && atomId < StatsdStats::kMaxAtomTag;
}
+inline bool isPulledAtom(int atomId) {
+ return atomId >= StatsdStats::kPullAtomStartTag && atomId < StatsdStats::kVendorAtomStartTag;
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/statscompanion_util.cpp b/cmds/statsd/src/statscompanion_util.cpp
index d338827774f9..ce07ec0ea884 100644
--- a/cmds/statsd/src/statscompanion_util.cpp
+++ b/cmds/statsd/src/statscompanion_util.cpp
@@ -18,26 +18,16 @@
#include "Log.h"
#include "statscompanion_util.h"
+#include <android/binder_auto_utils.h>
+#include <android/binder_manager.h>
namespace android {
namespace os {
namespace statsd {
-sp <IStatsCompanionService> getStatsCompanionService() {
- sp<IStatsCompanionService> statsCompanion = nullptr;
- // Get statscompanion service from service manager
- static const sp <IServiceManager> sm(defaultServiceManager());
- if (statsCompanion == nullptr) {
- if (sm != nullptr) {
- const String16 name("statscompanion");
- statsCompanion = interface_cast<IStatsCompanionService>(sm->checkService(name));
- if (statsCompanion == nullptr) {
- ALOGW("statscompanion service unavailable!");
- return nullptr;
- }
- }
- }
- return statsCompanion;
+shared_ptr<IStatsCompanionService> getStatsCompanionService() {
+ ::ndk::SpAIBinder binder(AServiceManager_getService("statscompanion"));
+ return IStatsCompanionService::fromBinder(binder);
}
} // namespace statsd
diff --git a/cmds/statsd/src/statscompanion_util.h b/cmds/statsd/src/statscompanion_util.h
index dc4f28361214..e20c40bba104 100644
--- a/cmds/statsd/src/statscompanion_util.h
+++ b/cmds/statsd/src/statscompanion_util.h
@@ -16,14 +16,17 @@
#pragma once
-#include "StatsLogProcessor.h"
+#include <aidl/android/os/IStatsCompanionService.h>
+
+using aidl::android::os::IStatsCompanionService;
+using std::shared_ptr;
namespace android {
namespace os {
namespace statsd {
/** Fetches and returns the StatsCompanionService. */
-sp<IStatsCompanionService> getStatsCompanionService();
+shared_ptr<IStatsCompanionService> getStatsCompanionService();
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 79c06b98a82d..acdffd3d4712 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -35,7 +35,7 @@ enum Position {
enum TimeUnit {
TIME_UNIT_UNSPECIFIED = 0;
- ONE_MINUTE = 1;
+ ONE_MINUTE = 1; // WILL BE GUARDRAILED TO 5 MINS UNLESS UID = SHELL OR ROOT
FIVE_MINUTES = 2;
TEN_MINUTES = 3;
THIRTY_MINUTES = 4;
@@ -44,6 +44,7 @@ enum TimeUnit {
SIX_HOURS = 7;
TWELVE_HOURS = 8;
ONE_DAY = 9;
+ ONE_WEEK = 10;
CTS = 1000;
}
@@ -130,7 +131,7 @@ message SimplePredicate {
UNKNOWN = 0;
FALSE = 1;
}
- optional InitialValue initial_value = 5 [default = FALSE];
+ optional InitialValue initial_value = 5 [default = UNKNOWN];
optional FieldMatcher dimensions = 6;
}
@@ -150,6 +151,24 @@ message Predicate {
}
}
+message StateMap {
+ message StateGroup {
+ optional int64 group_id = 1;
+
+ repeated int32 value = 2;
+ }
+
+ repeated StateGroup group = 1;
+}
+
+message State {
+ optional int64 id = 1;
+
+ optional int32 atom_id = 2;
+
+ optional StateMap map = 3;
+}
+
message MetricConditionLink {
optional int64 condition = 1;
@@ -158,6 +177,14 @@ message MetricConditionLink {
optional FieldMatcher fields_in_condition = 3;
}
+message MetricStateLink {
+ optional int32 state_atom_id = 1;
+
+ optional FieldMatcher fields_in_what = 2;
+
+ optional FieldMatcher fields_in_state = 3;
+}
+
message FieldFilter {
optional bool include_all = 1 [default = false];
optional FieldMatcher fields = 2;
@@ -171,6 +198,9 @@ message EventMetric {
optional int64 condition = 3;
repeated MetricConditionLink links = 4;
+
+ reserved 100;
+ reserved 101;
}
message CountMetric {
@@ -182,11 +212,18 @@ message CountMetric {
optional FieldMatcher dimensions_in_what = 4;
- optional FieldMatcher dimensions_in_condition = 7;
+ repeated int64 slice_by_state = 8;
optional TimeUnit bucket = 5;
repeated MetricConditionLink links = 6;
+
+ repeated MetricStateLink state_link = 9;
+
+ optional FieldMatcher dimensions_in_condition = 7 [deprecated = true];
+
+ reserved 100;
+ reserved 101;
}
message DurationMetric {
@@ -196,8 +233,12 @@ message DurationMetric {
optional int64 condition = 3;
+ repeated int64 slice_by_state = 9;
+
repeated MetricConditionLink links = 4;
+ repeated MetricStateLink state_link = 10;
+
enum AggregationType {
SUM = 1;
@@ -207,9 +248,12 @@ message DurationMetric {
optional FieldMatcher dimensions_in_what = 6;
- optional FieldMatcher dimensions_in_condition = 8;
-
optional TimeUnit bucket = 7;
+
+ optional FieldMatcher dimensions_in_condition = 8 [deprecated = true];
+
+ reserved 100;
+ reserved 101;
}
message GaugeMetric {
@@ -225,7 +269,7 @@ message GaugeMetric {
optional FieldMatcher dimensions_in_what = 5;
- optional FieldMatcher dimensions_in_condition = 8;
+ optional FieldMatcher dimensions_in_condition = 8 [deprecated = true];
optional TimeUnit bucket = 6;
@@ -243,9 +287,12 @@ message GaugeMetric {
optional int64 max_num_gauge_atoms_per_bucket = 11 [default = 10];
- optional int32 max_pull_delay_sec = 13 [default = 10];
+ optional int32 max_pull_delay_sec = 13 [default = 30];
optional bool split_bucket_for_app_upgrade = 14 [default = true];
+
+ reserved 100;
+ reserved 101;
}
message ValueMetric {
@@ -259,12 +306,14 @@ message ValueMetric {
optional FieldMatcher dimensions_in_what = 5;
- optional FieldMatcher dimensions_in_condition = 9;
+ repeated int64 slice_by_state = 18;
optional TimeUnit bucket = 6;
repeated MetricConditionLink links = 7;
+ repeated MetricStateLink state_link = 19;
+
enum AggregationType {
SUM = 1;
MIN = 2;
@@ -291,9 +340,14 @@ message ValueMetric {
optional bool skip_zero_diff_output = 14 [default = true];
- optional int32 max_pull_delay_sec = 16 [default = 10];
+ optional int32 max_pull_delay_sec = 16 [default = 30];
optional bool split_bucket_for_app_upgrade = 17 [default = true];
+
+ optional FieldMatcher dimensions_in_condition = 9 [deprecated = true];
+
+ reserved 100;
+ reserved 101;
}
message Alert {
@@ -393,6 +447,12 @@ message MetricActivation {
repeated EventActivation event_activation = 2;
}
+message PullAtomPackages {
+ optional int32 atom_id = 1;
+
+ repeated string packages = 2;
+}
+
message StatsdConfig {
optional int64 id = 1;
@@ -438,6 +498,14 @@ message StatsdConfig {
optional bool persist_locally = 20 [default = false];
+ repeated State state = 21;
+
+ repeated string default_pull_packages = 22;
+
+ repeated PullAtomPackages pull_atom_packages = 23;
+
+ repeated int32 whitelisted_atom_ids = 24;
+
// Field number 1000 is reserved for later use.
reserved 1000;
}
diff --git a/cmds/statsd/src/statsd_metadata.proto b/cmds/statsd/src/statsd_metadata.proto
new file mode 100644
index 000000000000..200b392f7542
--- /dev/null
+++ b/cmds/statsd/src/statsd_metadata.proto
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 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.metadata;
+
+message ConfigKey {
+ optional int64 config_id = 1;
+ optional int32 uid = 2;
+}
+
+message Field {
+ optional int32 tag = 1;
+ optional int32 field = 2;
+}
+
+message FieldValue {
+ optional Field field = 1;
+ oneof value {
+ int32 value_int = 2;
+ int64 value_long = 3;
+ float value_float = 4;
+ double value_double = 5;
+ string value_str = 6;
+ bytes value_storage = 7;
+ }
+}
+
+message MetricDimensionKey {
+ repeated FieldValue dimension_key_in_what = 1;
+ repeated FieldValue state_values_key = 2;
+}
+
+message AlertDimensionKeyedData {
+ // The earliest time the alert can be fired again in wall clock time.
+ optional int32 last_refractory_ends_sec = 1;
+ optional MetricDimensionKey dimension_key = 2;
+}
+
+message AlertMetadata {
+ optional int64 alert_id = 1;
+ repeated AlertDimensionKeyedData alert_dim_keyed_data = 2;
+}
+
+// All metadata for a config in statsd
+message StatsMetadata {
+ optional ConfigKey config_key = 1;
+ repeated AlertMetadata alert_metadata = 2;
+}
+
+message StatsMetadataList {
+ repeated StatsMetadata stats_metadata = 1;
+} \ No newline at end of file
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 507297c6c401..dcfdfe3aae53 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -44,7 +44,7 @@ using std::map;
#define TRAIN_INFO_PATH "/data/misc/train-info/train-info.bin"
// Magic word at the start of the train info file, change this if changing the file format
-const uint32_t TRAIN_INFO_FILE_MAGIC = 0xff7447ff;
+const uint32_t TRAIN_INFO_FILE_MAGIC = 0xfb7447bf;
// for ConfigMetricsReportList
const int FIELD_ID_REPORTS = 2;
@@ -75,6 +75,29 @@ string StorageManager::getDataHistoryFileName(long wallClockSec, int uid, int64_
(long long)id);
}
+static string findTrainInfoFileNameLocked(const string& trainName) {
+ unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir);
+ if (dir == NULL) {
+ VLOG("Path %s does not exist", TRAIN_INFO_DIR);
+ return "";
+ }
+ dirent* de;
+ while ((de = readdir(dir.get()))) {
+ char* fileName = de->d_name;
+ if (fileName[0] == '.') continue;
+
+ size_t fileNameLength = strlen(fileName);
+ if (fileNameLength >= trainName.length()) {
+ if (0 == strncmp(fileName + fileNameLength - trainName.length(), trainName.c_str(),
+ trainName.length())) {
+ return string(fileName);
+ }
+ }
+ }
+
+ return "";
+}
+
// Returns array of int64_t which contains timestamp in seconds, uid,
// configID and whether the file is a local history file.
static void parseFileName(char* name, FileName* output) {
@@ -123,20 +146,25 @@ void StorageManager::writeFile(const char* file, const void* buffer, int numByte
close(fd);
}
-bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string& trainName,
- int32_t status, const std::vector<int64_t>& experimentIds) {
+bool StorageManager::writeTrainInfo(const InstallTrainInfo& trainInfo) {
std::lock_guard<std::mutex> lock(sTrainInfoMutex);
- deleteAllFiles(TRAIN_INFO_DIR);
+ if (trainInfo.trainName.empty()) {
+ return false;
+ }
+ deleteSuffixedFiles(TRAIN_INFO_DIR, trainInfo.trainName.c_str());
+
+ std::string fileName =
+ StringPrintf("%s/%ld_%s", TRAIN_INFO_DIR, (long) getWallClockSec(),
+ trainInfo.trainName.c_str());
- int fd = open(TRAIN_INFO_PATH, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
+ int fd = open(fileName.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
if (fd == -1) {
- VLOG("Attempt to access %s but failed", TRAIN_INFO_PATH);
+ VLOG("Attempt to access %s but failed", fileName.c_str());
return false;
}
size_t result;
-
// Write the magic word
result = write(fd, &TRAIN_INFO_FILE_MAGIC, sizeof(TRAIN_INFO_FILE_MAGIC));
if (result != sizeof(TRAIN_INFO_FILE_MAGIC)) {
@@ -146,8 +174,8 @@ bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string&
}
// Write the train version
- const size_t trainVersionCodeByteCount = sizeof(trainVersionCode);
- result = write(fd, &trainVersionCode, trainVersionCodeByteCount);
+ const size_t trainVersionCodeByteCount = sizeof(trainInfo.trainVersionCode);
+ result = write(fd, &trainInfo.trainVersionCode, trainVersionCodeByteCount);
if (result != trainVersionCodeByteCount) {
VLOG("Failed to wrtie train version code");
close(fd);
@@ -155,7 +183,7 @@ bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string&
}
// Write # of bytes in trainName to file
- const size_t trainNameSize = trainName.size();
+ const size_t trainNameSize = trainInfo.trainName.size();
const size_t trainNameSizeByteCount = sizeof(trainNameSize);
result = write(fd, (uint8_t*)&trainNameSize, trainNameSizeByteCount);
if (result != trainNameSizeByteCount) {
@@ -165,7 +193,7 @@ bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string&
}
// Write trainName to file
- result = write(fd, trainName.c_str(), trainNameSize);
+ result = write(fd, trainInfo.trainName.c_str(), trainNameSize);
if (result != trainNameSize) {
VLOG("Failed to write train name");
close(fd);
@@ -173,8 +201,8 @@ bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string&
}
// Write status to file
- const size_t statusByteCount = sizeof(status);
- result = write(fd, (uint8_t*)&status, statusByteCount);
+ const size_t statusByteCount = sizeof(trainInfo.status);
+ result = write(fd, (uint8_t*)&trainInfo.status, statusByteCount);
if (result != statusByteCount) {
VLOG("Failed to write status");
close(fd);
@@ -182,7 +210,7 @@ bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string&
}
// Write experiment id count to file.
- const size_t experimentIdsCount = experimentIds.size();
+ const size_t experimentIdsCount = trainInfo.experimentIds.size();
const size_t experimentIdsCountByteCount = sizeof(experimentIdsCount);
result = write(fd, (uint8_t*) &experimentIdsCount, experimentIdsCountByteCount);
if (result != experimentIdsCountByteCount) {
@@ -193,7 +221,7 @@ bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string&
// Write experimentIds to file
for (size_t i = 0; i < experimentIdsCount; i++) {
- const int64_t experimentId = experimentIds[i];
+ const int64_t experimentId = trainInfo.experimentIds[i];
const size_t experimentIdByteCount = sizeof(experimentId);
result = write(fd, &experimentId, experimentIdByteCount);
if (result == experimentIdByteCount) {
@@ -205,23 +233,47 @@ bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string&
}
}
- result = fchown(fd, AID_STATSD, AID_STATSD);
- if (result) {
- VLOG("Failed to chown train info file to statsd");
- close(fd);
- return false;
+ // Write bools to file
+ const size_t boolByteCount = sizeof(trainInfo.requiresStaging);
+ result = write(fd, (uint8_t*)&trainInfo.requiresStaging, boolByteCount);
+ if (result != boolByteCount) {
+ VLOG("Failed to write requires staging");
+ close(fd);
+ return false;
+ }
+
+ result = write(fd, (uint8_t*)&trainInfo.rollbackEnabled, boolByteCount);
+ if (result != boolByteCount) {
+ VLOG("Failed to write rollback enabled");
+ close(fd);
+ return false;
+ }
+
+ result = write(fd, (uint8_t*)&trainInfo.requiresLowLatencyMonitor, boolByteCount);
+ if (result != boolByteCount) {
+ VLOG("Failed to write requires log latency monitor");
+ close(fd);
+ return false;
}
close(fd);
return true;
}
-bool StorageManager::readTrainInfo(InstallTrainInfo& trainInfo) {
+bool StorageManager::readTrainInfo(const std::string& trainName, InstallTrainInfo& trainInfo) {
std::lock_guard<std::mutex> lock(sTrainInfoMutex);
+ return readTrainInfoLocked(trainName, trainInfo);
+}
- int fd = open(TRAIN_INFO_PATH, O_RDONLY | O_CLOEXEC);
+bool StorageManager::readTrainInfoLocked(const std::string& trainName, InstallTrainInfo& trainInfo) {
+ trimToFit(TRAIN_INFO_DIR, /*parseTimestampOnly=*/ true);
+ string fileName = findTrainInfoFileNameLocked(trainName);
+ if (fileName.empty()) {
+ return false;
+ }
+ int fd = open(StringPrintf("%s/%s", TRAIN_INFO_DIR, fileName.c_str()).c_str(), O_RDONLY | O_CLOEXEC);
if (fd == -1) {
- VLOG("Failed to open train-info.bin");
+ VLOG("Failed to open %s", fileName.c_str());
return false;
}
@@ -297,6 +349,29 @@ bool StorageManager::readTrainInfo(InstallTrainInfo& trainInfo) {
trainInfo.experimentIds.push_back(experimentId);
}
+ // Read bools
+ const size_t boolByteCount = sizeof(trainInfo.requiresStaging);
+ result = read(fd, &trainInfo.requiresStaging, boolByteCount);
+ if (result != boolByteCount) {
+ VLOG("Failed to read requires requires staging from train info file");
+ close(fd);
+ return false;
+ }
+
+ result = read(fd, &trainInfo.rollbackEnabled, boolByteCount);
+ if (result != boolByteCount) {
+ VLOG("Failed to read requires rollback enabled from train info file");
+ close(fd);
+ return false;
+ }
+
+ result = read(fd, &trainInfo.requiresLowLatencyMonitor, boolByteCount);
+ if (result != boolByteCount) {
+ VLOG("Failed to read requires requires low latency monitor from train info file");
+ close(fd);
+ return false;
+ }
+
// Expect to be at EOF.
char c;
result = read(fd, &c, 1);
@@ -311,6 +386,32 @@ bool StorageManager::readTrainInfo(InstallTrainInfo& trainInfo) {
return true;
}
+vector<InstallTrainInfo> StorageManager::readAllTrainInfo() {
+ std::lock_guard<std::mutex> lock(sTrainInfoMutex);
+ vector<InstallTrainInfo> trainInfoList;
+ unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir);
+ if (dir == NULL) {
+ VLOG("Directory does not exist: %s", TRAIN_INFO_DIR);
+ return trainInfoList;
+ }
+
+ dirent* de;
+ while ((de = readdir(dir.get()))) {
+ char* name = de->d_name;
+ if (name[0] == '.') {
+ continue;
+ }
+
+ InstallTrainInfo trainInfo;
+ bool readSuccess = StorageManager::readTrainInfoLocked(name, trainInfo);
+ if (!readSuccess) {
+ continue;
+ }
+ trainInfoList.push_back(trainInfo);
+ }
+ return trainInfoList;
+}
+
void StorageManager::deleteFile(const char* file) {
if (remove(file) != 0) {
VLOG("Attempt to delete %s but is not found", file);
@@ -574,7 +675,7 @@ void StorageManager::sortFiles(vector<FileInfo>* fileNames) {
});
}
-void StorageManager::trimToFit(const char* path) {
+void StorageManager::trimToFit(const char* path, bool parseTimestampOnly) {
unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
if (dir == NULL) {
VLOG("Path %s does not exist", path);
@@ -589,9 +690,16 @@ void StorageManager::trimToFit(const char* path) {
if (name[0] == '.') continue;
FileName output;
- parseFileName(name, &output);
+ string file_name;
+ if (parseTimestampOnly) {
+ file_name = StringPrintf("%s/%s", path, name);
+ output.mTimestampSec = StrToInt64(strtok(name, "_"));
+ output.mIsHistory = false;
+ } else {
+ parseFileName(name, &output);
+ file_name = output.getFullFileName(path);
+ }
if (output.mTimestampSec == -1) continue;
- string file_name = output.getFullFileName(path);
// Check for timestamp and delete if it's too old.
long fileAge = nowSec - output.mTimestampSec;
diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h
index 69b41c2cb974..d59046dfbb99 100644
--- a/cmds/statsd/src/storage/StorageManager.h
+++ b/cmds/statsd/src/storage/StorageManager.h
@@ -52,13 +52,22 @@ public:
/**
* Writes train info.
*/
- static bool writeTrainInfo(int64_t trainVersionCode, const std::string& trainName,
- int32_t status, const std::vector<int64_t>& experimentIds);
+ static bool writeTrainInfo(const InstallTrainInfo& trainInfo);
/**
* Reads train info.
*/
- static bool readTrainInfo(InstallTrainInfo& trainInfo);
+ static bool readTrainInfo(const std::string& trainName, InstallTrainInfo& trainInfo);
+
+ /**
+ * Reads train info assuming lock is obtained.
+ */
+ static bool readTrainInfoLocked(const std::string& trainName, InstallTrainInfo& trainInfo);
+
+ /**
+ * Reads all train info and returns a vector of train info.
+ */
+ static vector<InstallTrainInfo> readAllTrainInfo();
/**
* Reads the file content to the buffer.
@@ -124,7 +133,7 @@ public:
* Trims files in the provided directory to limit the total size, number of
* files, accumulation of outdated files.
*/
- static void trimToFit(const char* dir);
+ static void trimToFit(const char* dir, bool parseTimestampOnly = false);
/**
* Returns true if there already exists identical configuration on device.
diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
index ba5e66761528..1d77513d9d33 100644
--- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp
+++ b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
@@ -21,10 +21,8 @@
#include "packages/UidMap.h"
#include "stats_log_util.h"
-#include <android/os/IIncidentManager.h>
-#include <android/os/IncidentReportArgs.h>
#include <android/util/ProtoOutputStream.h>
-#include <binder/IServiceManager.h>
+#include <incident/incident_report.h>
#include <vector>
@@ -51,7 +49,6 @@ const int FIELD_ID_TRIGGER_DETAILS = 4;
const int FIELD_ID_TRIGGER_DETAILS_TRIGGER_METRIC = 1;
const int FIELD_ID_METRIC_VALUE_METRIC_ID = 1;
const int FIELD_ID_METRIC_VALUE_DIMENSION_IN_WHAT = 2;
-const int FIELD_ID_METRIC_VALUE_DIMENSION_IN_CONDITION = 3;
const int FIELD_ID_METRIC_VALUE_VALUE = 4;
const int FIELD_ID_PACKAGE_INFO = 3;
@@ -83,10 +80,8 @@ void getProtoData(const int64_t& rule_id, int64_t metricId, const MetricDimensio
writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), nullptr, &headerProto);
headerProto.end(dimToken);
+ // deprecated field
// optional DimensionsValue dimension_in_condition = 3;
- dimToken = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_METRIC_VALUE_DIMENSION_IN_CONDITION);
- writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), nullptr, &headerProto);
- headerProto.end(dimToken);
// optional int64 value = 4;
headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_VALUE_VALUE, (long long)metricValue);
@@ -105,13 +100,6 @@ void getProtoData(const int64_t& rule_id, int64_t metricId, const MetricDimensio
}
}
- for (const auto& dim : dimensionKey.getDimensionKeyInCondition().getValues()) {
- int uid = getUidIfExists(dim);
- if (uid > 2000) {
- uids.insert(uid);
- }
- }
-
if (!uids.empty()) {
uint64_t token = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_PACKAGE_INFO);
UidMap::getInstance()->writeUidMapSnapshot(getElapsedRealtimeNs(), true, true, uids,
@@ -142,44 +130,38 @@ bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int
return false;
}
- IncidentReportArgs incidentReport;
+ AIncidentReportArgs* args = AIncidentReportArgs_init();
vector<uint8_t> protoData;
getProtoData(rule_id, metricId, dimensionKey, metricValue, configKey,
config.alert_description(), &protoData);
- incidentReport.addHeader(protoData);
+ AIncidentReportArgs_addHeader(args, protoData.data(), protoData.size());
for (int i = 0; i < config.section_size(); i++) {
- incidentReport.addSection(config.section(i));
+ AIncidentReportArgs_addSection(args, config.section(i));
}
uint8_t dest;
switch (config.dest()) {
case IncidentdDetails_Destination_AUTOMATIC:
- dest = android::os::PRIVACY_POLICY_AUTOMATIC;
+ dest = INCIDENT_REPORT_PRIVACY_POLICY_AUTOMATIC;
break;
case IncidentdDetails_Destination_EXPLICIT:
- dest = android::os::PRIVACY_POLICY_EXPLICIT;
+ dest = INCIDENT_REPORT_PRIVACY_POLICY_EXPLICIT;
break;
default:
- dest = android::os::PRIVACY_POLICY_AUTOMATIC;
+ dest = INCIDENT_REPORT_PRIVACY_POLICY_AUTOMATIC;
}
- incidentReport.setPrivacyPolicy(dest);
+ AIncidentReportArgs_setPrivacyPolicy(args, dest);
- incidentReport.setReceiverPkg(config.receiver_pkg());
+ AIncidentReportArgs_setReceiverPackage(args, config.receiver_pkg().c_str());
- incidentReport.setReceiverCls(config.receiver_cls());
+ AIncidentReportArgs_setReceiverClass(args, config.receiver_cls().c_str());
- sp<IIncidentManager> service = interface_cast<IIncidentManager>(
- defaultServiceManager()->getService(android::String16("incident")));
- if (service == nullptr) {
- ALOGW("Failed to fetch incident service.");
- return false;
- }
- VLOG("Calling incidentd %p", service.get());
- binder::Status s = service->reportIncident(incidentReport);
- VLOG("Report incident status: %s", s.toString8().string());
- return s.isOk();
+ int err = AIncidentReportArgs_takeReport(args);
+ AIncidentReportArgs_delete(args);
+
+ return err == NO_ERROR;
}
} // namespace statsd
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
index d4f44780b0bc..c915ef3bf069 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
@@ -19,7 +19,6 @@
#include "SubscriberReporter.h"
-using android::IBinder;
using std::lock_guard;
namespace android {
@@ -28,18 +27,66 @@ namespace statsd {
using std::vector;
+struct BroadcastSubscriberDeathCookie {
+ BroadcastSubscriberDeathCookie(const ConfigKey& configKey, int64_t subscriberId,
+ const shared_ptr<IPendingIntentRef>& pir):
+ mConfigKey(configKey),
+ mSubscriberId(subscriberId),
+ mPir(pir) {}
+
+ ConfigKey mConfigKey;
+ int64_t mSubscriberId;
+ shared_ptr<IPendingIntentRef> mPir;
+};
+
+void SubscriberReporter::broadcastSubscriberDied(void* cookie) {
+ auto cookie_ = static_cast<BroadcastSubscriberDeathCookie*>(cookie);
+ ConfigKey& configKey = cookie_->mConfigKey;
+ int64_t subscriberId = cookie_->mSubscriberId;
+ shared_ptr<IPendingIntentRef>& pir = cookie_->mPir;
+
+ SubscriberReporter& thiz = getInstance();
+
+ // Erase the mapping from a (config_key, subscriberId) to a pir if the
+ // mapping exists.
+ lock_guard<mutex> lock(thiz.mLock);
+ auto subscriberMapIt = thiz.mIntentMap.find(configKey);
+ if (subscriberMapIt != thiz.mIntentMap.end()) {
+ auto subscriberMap = subscriberMapIt->second;
+ auto pirIt = subscriberMap.find(subscriberId);
+ if (pirIt != subscriberMap.end() && pirIt->second == pir) {
+ subscriberMap.erase(subscriberId);
+ if (subscriberMap.empty()) {
+ thiz.mIntentMap.erase(configKey);
+ }
+ }
+ }
+
+ // The death recipient corresponding to this specific pir can never be
+ // triggered again, so free up resources.
+ delete cookie_;
+}
+
+SubscriberReporter::SubscriberReporter() :
+ mBroadcastSubscriberDeathRecipient(AIBinder_DeathRecipient_new(broadcastSubscriberDied)) {
+}
+
void SubscriberReporter::setBroadcastSubscriber(const ConfigKey& configKey,
int64_t subscriberId,
- const sp<IBinder>& intentSender) {
+ const shared_ptr<IPendingIntentRef>& pir) {
VLOG("SubscriberReporter::setBroadcastSubscriber called.");
- lock_guard<std::mutex> lock(mLock);
- mIntentMap[configKey][subscriberId] = intentSender;
+ {
+ lock_guard<mutex> lock(mLock);
+ mIntentMap[configKey][subscriberId] = pir;
+ }
+ AIBinder_linkToDeath(pir->asBinder().get(), mBroadcastSubscriberDeathRecipient.get(),
+ new BroadcastSubscriberDeathCookie(configKey, subscriberId, pir));
}
void SubscriberReporter::unsetBroadcastSubscriber(const ConfigKey& configKey,
int64_t subscriberId) {
VLOG("SubscriberReporter::unsetBroadcastSubscriber called.");
- lock_guard<std::mutex> lock(mLock);
+ lock_guard<mutex> lock(mLock);
auto subscriberMapIt = mIntentMap.find(configKey);
if (subscriberMapIt != mIntentMap.end()) {
subscriberMapIt->second.erase(subscriberId);
@@ -49,12 +96,6 @@ void SubscriberReporter::unsetBroadcastSubscriber(const ConfigKey& configKey,
}
}
-void SubscriberReporter::removeConfig(const ConfigKey& configKey) {
- VLOG("SubscriberReporter::removeConfig called.");
- lock_guard<std::mutex> lock(mLock);
- mIntentMap.erase(configKey);
-}
-
void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey,
const Subscription& subscription,
const MetricDimensionKey& dimKey) const {
@@ -67,7 +108,7 @@ void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey,
// config id - the name of this config (for this particular uid)
VLOG("SubscriberReporter::alertBroadcastSubscriber called.");
- lock_guard<std::mutex> lock(mLock);
+ lock_guard<mutex> lock(mLock);
if (!subscription.has_broadcast_subscriber_details()
|| !subscription.broadcast_subscriber_details().has_subscriber_id()) {
@@ -76,10 +117,10 @@ void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey,
}
int64_t subscriberId = subscription.broadcast_subscriber_details().subscriber_id();
- vector<String16> cookies;
+ vector<string> cookies;
cookies.reserve(subscription.broadcast_subscriber_details().cookie_size());
for (auto& cookie : subscription.broadcast_subscriber_details().cookie()) {
- cookies.push_back(String16(cookie.c_str()));
+ cookies.push_back(cookie);
}
auto it1 = mIntentMap.find(configKey);
@@ -96,79 +137,33 @@ void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey,
sendBroadcastLocked(it2->second, configKey, subscription, cookies, dimKey);
}
-void SubscriberReporter::sendBroadcastLocked(const sp<IBinder>& intentSender,
+void SubscriberReporter::sendBroadcastLocked(const shared_ptr<IPendingIntentRef>& pir,
const ConfigKey& configKey,
const Subscription& subscription,
- const vector<String16>& cookies,
+ const vector<string>& cookies,
const MetricDimensionKey& dimKey) const {
VLOG("SubscriberReporter::sendBroadcastLocked called.");
- if (mStatsCompanionService == nullptr) {
- ALOGW("Failed to send subscriber broadcast: could not access StatsCompanionService.");
- return;
- }
- mStatsCompanionService->sendSubscriberBroadcast(
- intentSender,
+ pir->sendSubscriberBroadcast(
configKey.GetUid(),
configKey.GetId(),
subscription.id(),
subscription.rule_id(),
cookies,
- getStatsDimensionsValue(dimKey.getDimensionKeyInWhat()));
+ dimKey.getDimensionKeyInWhat().toStatsDimensionsValueParcel());
}
-void getStatsDimensionsValueHelper(const vector<FieldValue>& dims, size_t* index, int depth,
- int prefix, vector<StatsDimensionsValue>* output) {
- size_t count = dims.size();
- while (*index < count) {
- const auto& dim = dims[*index];
- const int valueDepth = dim.mField.getDepth();
- const int valuePrefix = dim.mField.getPrefix(depth);
- if (valueDepth > 2) {
- ALOGE("Depth > 2 not supported");
- return;
- }
- if (depth == valueDepth && valuePrefix == prefix) {
- switch (dim.mValue.getType()) {
- case INT:
- output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth),
- dim.mValue.int_value));
- break;
- case LONG:
- output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth),
- dim.mValue.long_value));
- break;
- case FLOAT:
- output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth),
- dim.mValue.float_value));
- break;
- case STRING:
- output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth),
- String16(dim.mValue.str_value.c_str())));
- break;
- default:
- break;
- }
- (*index)++;
- } else if (valueDepth > depth && valuePrefix == prefix) {
- vector<StatsDimensionsValue> childOutput;
- getStatsDimensionsValueHelper(dims, index, depth + 1, dim.mField.getPrefix(depth + 1),
- &childOutput);
- output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth), childOutput));
- } else {
- return;
- }
+shared_ptr<IPendingIntentRef> SubscriberReporter::getBroadcastSubscriber(const ConfigKey& configKey,
+ int64_t subscriberId) {
+ lock_guard<mutex> lock(mLock);
+ auto subscriberMapIt = mIntentMap.find(configKey);
+ if (subscriberMapIt == mIntentMap.end()) {
+ return nullptr;
}
-}
-
-StatsDimensionsValue SubscriberReporter::getStatsDimensionsValue(const HashableDimensionKey& dim) {
- if (dim.getValues().size() == 0) {
- return StatsDimensionsValue();
+ auto pirMapIt = subscriberMapIt->second.find(subscriberId);
+ if (pirMapIt == subscriberMapIt->second.end()) {
+ return nullptr;
}
-
- vector<StatsDimensionsValue> fields;
- size_t index = 0;
- getStatsDimensionsValueHelper(dim.getValues(), &index, 0, 0, &fields);
- return StatsDimensionsValue(dim.getValues()[0].mField.getTag(), fields);
+ return pirMapIt->second;
}
} // namespace statsd
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h
index 2a7f771a0ba4..4fe428198e71 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.h
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.h
@@ -16,18 +16,25 @@
#pragma once
-#include <android/os/IStatsCompanionService.h>
+#include <aidl/android/os/IPendingIntentRef.h>
#include <utils/RefBase.h>
+#include <utils/String16.h>
#include "config/ConfigKey.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // subscription
-#include "android/os/StatsDimensionsValue.h"
#include "HashableDimensionKey.h"
#include <mutex>
#include <unordered_map>
#include <vector>
+using aidl::android::os::IPendingIntentRef;
+using std::mutex;
+using std::shared_ptr;
+using std::string;
+using std::unordered_map;
+using std::vector;
+
namespace android {
namespace os {
namespace statsd {
@@ -47,32 +54,17 @@ public:
void operator=(SubscriberReporter const&) = delete;
/**
- * Tells SubscriberReporter what IStatsCompanionService to use.
- * May be nullptr, but SubscriberReporter will not send broadcasts for any calls
- * to alertBroadcastSubscriber that occur while nullptr.
- */
- void setStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) {
- std::lock_guard<std::mutex> lock(mLock);
- sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
- mStatsCompanionService = statsCompanionService;
- }
-
- /**
* Stores the given intentSender, associating it with the given (configKey, subscriberId) pair.
- * intentSender must be convertible into an IntentSender (in Java) using IntentSender(IBinder).
*/
void setBroadcastSubscriber(const ConfigKey& configKey,
int64_t subscriberId,
- const sp<android::IBinder>& intentSender);
+ const shared_ptr<IPendingIntentRef>& pir);
/**
* Erases any intentSender information from the given (configKey, subscriberId) pair.
*/
void unsetBroadcastSubscriber(const ConfigKey& configKey, int64_t subscriberId);
- /** Remove all information stored by SubscriberReporter about the given config. */
- void removeConfig(const ConfigKey& configKey);
-
/**
* Sends a broadcast via the intentSender previously stored for the
* given (configKey, subscriberId) pair by setBroadcastSubscriber.
@@ -82,29 +74,34 @@ public:
const Subscription& subscription,
const MetricDimensionKey& dimKey) const;
- static StatsDimensionsValue getStatsDimensionsValue(const HashableDimensionKey& dim);
+ shared_ptr<IPendingIntentRef> getBroadcastSubscriber(const ConfigKey& configKey,
+ int64_t subscriberId);
private:
- SubscriberReporter() {};
+ SubscriberReporter();
- mutable std::mutex mLock;
+ mutable mutex mLock;
- /** Binder interface for communicating with StatsCompanionService. */
- sp<IStatsCompanionService> mStatsCompanionService = nullptr;
-
- /** Maps <ConfigKey, SubscriberId> -> IBinder (which represents an IIntentSender). */
- std::unordered_map<ConfigKey,
- std::unordered_map<int64_t, sp<android::IBinder>>> mIntentMap;
+ /** Maps <ConfigKey, SubscriberId> -> IPendingIntentRef (which represents a PendingIntent). */
+ unordered_map<ConfigKey, unordered_map<int64_t, shared_ptr<IPendingIntentRef>>> mIntentMap;
/**
* Sends a broadcast via the given intentSender (using mStatsCompanionService), along
* with the information in the other parameters.
*/
- void sendBroadcastLocked(const sp<android::IBinder>& intentSender,
+ void sendBroadcastLocked(const shared_ptr<IPendingIntentRef>& pir,
const ConfigKey& configKey,
const Subscription& subscription,
- const std::vector<String16>& cookies,
+ const vector<string>& cookies,
const MetricDimensionKey& dimKey) const;
+
+ ::ndk::ScopedAIBinder_DeathRecipient mBroadcastSubscriberDeathRecipient;
+
+ /**
+ * Death recipient callback that is called when a broadcast subscriber dies.
+ * The cookie is a pointer to a BroadcastSubscriberDeathCookie.
+ */
+ static void broadcastSubscriberDied(void* cookie);
};
} // namespace statsd
diff --git a/cmds/statsd/src/utils/MultiConditionTrigger.cpp b/cmds/statsd/src/utils/MultiConditionTrigger.cpp
new file mode 100644
index 000000000000..43a69337f368
--- /dev/null
+++ b/cmds/statsd/src/utils/MultiConditionTrigger.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+#define DEBUG false // STOPSHIP if true
+
+#include "MultiConditionTrigger.h"
+
+#include <thread>
+
+using namespace std;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+MultiConditionTrigger::MultiConditionTrigger(const set<string>& conditionNames,
+ function<void()> trigger)
+ : mRemainingConditionNames(conditionNames),
+ mTrigger(trigger),
+ mCompleted(mRemainingConditionNames.empty()) {
+ if (mCompleted) {
+ thread executorThread([this] { mTrigger(); });
+ executorThread.detach();
+ }
+}
+
+void MultiConditionTrigger::markComplete(const string& conditionName) {
+ bool doTrigger = false;
+ {
+ lock_guard<mutex> lg(mMutex);
+ if (mCompleted) {
+ return;
+ }
+ mRemainingConditionNames.erase(conditionName);
+ mCompleted = mRemainingConditionNames.empty();
+ doTrigger = mCompleted;
+ }
+ if (doTrigger) {
+ std::thread executorThread([this] { mTrigger(); });
+ executorThread.detach();
+ }
+}
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/utils/MultiConditionTrigger.h b/cmds/statsd/src/utils/MultiConditionTrigger.h
new file mode 100644
index 000000000000..51f6029915be
--- /dev/null
+++ b/cmds/statsd/src/utils/MultiConditionTrigger.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 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 <gtest/gtest_prod.h>
+
+#include <mutex>
+#include <set>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * This class provides a utility to wait for a set of named conditions to occur.
+ *
+ * It will execute the trigger runnable in a detached thread once all conditions have been marked
+ * true.
+ */
+class MultiConditionTrigger {
+public:
+ explicit MultiConditionTrigger(const std::set<std::string>& conditionNames,
+ std::function<void()> trigger);
+
+ MultiConditionTrigger(const MultiConditionTrigger&) = delete;
+ MultiConditionTrigger& operator=(const MultiConditionTrigger&) = delete;
+
+ // Mark a specific condition as true. If this condition has called markComplete already or if
+ // the event was not specified in the constructor, the function is a no-op.
+ void markComplete(const std::string& eventName);
+
+private:
+ mutable std::mutex mMutex;
+ std::set<std::string> mRemainingConditionNames;
+ std::function<void()> mTrigger;
+ bool mCompleted;
+
+ FRIEND_TEST(MultiConditionTriggerTest, TestCountDownCalledBySameEventName);
+};
+} // namespace statsd
+} // namespace os
+} // namespace android