diff options
Diffstat (limited to 'cmds/statsd/src')
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, ¤tTrueConditionDimensions); - trueDimensionsToProcess = ¤tTrueConditionDimensions; - } 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 |