diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h index 92e09ea0f8f91..e251399776fb6 100644 --- a/cmds/statsd/src/FieldValue.h +++ b/cmds/statsd/src/FieldValue.h @@ -181,6 +181,7 @@ public: return false; } + bool matches(const Matcher& that) const; }; @@ -360,7 +361,9 @@ struct Value { class Annotations { public: - Annotations() {} + 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. @@ -379,7 +382,9 @@ public: inline void setUidField(bool isUid) { setBitmaskAtPos(UID_POS, isUid); } - inline void setResetState(int resetState) { mResetState = resetState; } + inline void setResetState(int32_t resetState) { + mResetState = resetState; + } // Default value = false inline bool isNested() const { return getValueFromBitmask(NESTED_POS); } @@ -395,7 +400,9 @@ public: // If a reset state is not sent in the StatsEvent, returns -1. Note that a // reset satate is only sent if and only if a reset should be triggered. - inline int getResetState() const { return mResetState; } + inline int32_t getResetState() const { + return mResetState; + } private: inline void setBitmaskAtPos(int pos, bool value) { @@ -411,7 +418,7 @@ private: // there are only 4 booleans, just one byte is required. uint8_t mBooleanBitmask = 0; - int mResetState = -1; + int32_t mResetState = -1; }; /** diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp index 29249f4a6c552..eba66e0cb7b0e 100644 --- a/cmds/statsd/src/HashableDimensionKey.cpp +++ b/cmds/statsd/src/HashableDimensionKey.cpp @@ -180,6 +180,23 @@ bool filterValues(const vector& matcherFields, const vector return num_matches > 0; } +bool filterPrimaryKey(const std::vector& 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& matcherFields, const std::vector& values, std::vector* output) { for (const auto& field : matcherFields) { diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h index 33a5024977466..bd011005a301e 100644 --- a/cmds/statsd/src/HashableDimensionKey.h +++ b/cmds/statsd/src/HashableDimensionKey.h @@ -153,6 +153,18 @@ bool filterValues(const Matcher& matcherField, const std::vector& va bool filterValues(const std::vector& matcherFields, const std::vector& values, 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& values, HashableDimensionKey* output); + /** * Filter the values from FieldValues using the matchers. * diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index 8b6a864641559..61cd01728ab1d 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -293,7 +293,8 @@ void LogEvent::parseExclusiveStateAnnotation(uint8_t annotationType) { } const bool exclusiveState = readNextValue(); - mValues[mValues.size() - 1].mAnnotations.setExclusiveState(exclusiveState); + mExclusiveStateFieldIndex = mValues.size() - 1; + mValues[getExclusiveStateFieldIndex()].mAnnotations.setExclusiveState(exclusiveState); } void LogEvent::parseTriggerStateResetAnnotation(uint8_t annotationType) { diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index 4eeb7d64a463a..c9c0305dea4db 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -170,6 +170,20 @@ public: return mAttributionChainIndex; } + // 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 mExclusiveStateFieldIndex; + } + inline LogEvent makeCopy() { return LogEvent(*this); } @@ -297,6 +311,7 @@ private: bool mTruncateTimestamp = false; int mUidFieldIndex = -1; int mAttributionChainIndex = -1; + int mExclusiveStateFieldIndex = -1; }; void writeExperimentIdsToProto(const std::vector& experimentIds, std::vector* protoOut); diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 4550e65b64388..6aba13ca78597 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -451,7 +451,7 @@ protected: std::vector mSlicedStateAtoms; // Maps atom ids and state values to group_ids (>). - std::unordered_map> mStateGroupMap; + const std::unordered_map> mStateGroupMap; // MetricStateLinks defined in statsd_config that link fields in the state // atom to fields in the "what" atom. diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 2fcb13b709f93..88616dde61b76 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -795,9 +795,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t for (const auto& it : allMetricProducers) { // Register metrics to StateTrackers for (int atomId : it->getSlicedStateAtoms()) { - if (!StateManager::getInstance().registerListener(atomId, it)) { - return false; - } + StateManager::getInstance().registerListener(atomId, it); } } return true; diff --git a/cmds/statsd/src/state/StateManager.cpp b/cmds/statsd/src/state/StateManager.cpp index ea776fae05833..5514b446b3066 100644 --- a/cmds/statsd/src/state/StateManager.cpp +++ b/cmds/statsd/src/state/StateManager.cpp @@ -38,20 +38,12 @@ void StateManager::onLogEvent(const LogEvent& event) { } } -bool StateManager::registerListener(const int32_t atomId, wp listener) { +void StateManager::registerListener(const int32_t atomId, wp listener) { // Check if state tracker already exists. if (mStateTrackers.find(atomId) == mStateTrackers.end()) { - // Create a new state tracker iff atom is a state atom. - auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(atomId); - if (it != android::util::AtomsInfo::kStateAtomsFieldOptions.end()) { - mStateTrackers[atomId] = new StateTracker(atomId, it->second); - } else { - ALOGE("StateManager cannot register listener, Atom %d is not a state atom", atomId); - return false; - } + mStateTrackers[atomId] = new StateTracker(atomId); } mStateTrackers[atomId]->registerListener(listener); - return true; } void StateManager::unregisterListener(const int32_t atomId, wp listener) { diff --git a/cmds/statsd/src/state/StateManager.h b/cmds/statsd/src/state/StateManager.h index 8b3a4218238cc..577a0f51e38b8 100644 --- a/cmds/statsd/src/state/StateManager.h +++ b/cmds/statsd/src/state/StateManager.h @@ -45,10 +45,11 @@ public: // Notifies the correct StateTracker of an event. void onLogEvent(const LogEvent& event); - // Returns true if atomId is being tracked and is associated with a state - // atom. StateManager notifies the correct StateTracker to register listener. + // Notifies the StateTracker for the given atomId to register listener. // If the correct StateTracker does not exist, a new StateTracker is created. - bool registerListener(const int32_t atomId, wp listener); + // 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 listener); // Notifies the correct StateTracker to unregister a listener // and removes the tracker if it no longer has any listeners. diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp index ab861275073c7..b7f314a819df8 100644 --- a/cmds/statsd/src/state/StateTracker.cpp +++ b/cmds/statsd/src/state/StateTracker.cpp @@ -25,81 +25,43 @@ namespace android { namespace os { namespace statsd { -StateTracker::StateTracker(const int32_t atomId, const util::StateAtomFieldOptions& stateAtomInfo) - : mAtomId(atomId), - mStateField(getSimpleMatcher(atomId, stateAtomInfo.exclusiveField)), - mNested(stateAtomInfo.nested) { - // create matcher for each primary field - for (const auto& primaryField : stateAtomInfo.primaryFields) { - if (primaryField == util::FIRST_UID_IN_CHAIN) { - Matcher matcher = getFirstUidMatcher(atomId); - mPrimaryFields.push_back(matcher); - } else { - Matcher matcher = getSimpleMatcher(atomId, primaryField); - mPrimaryFields.push_back(matcher); - } - } - - if (stateAtomInfo.defaultState != util::UNSET_VALUE) { - mDefaultState = stateAtomInfo.defaultState; - } - - if (stateAtomInfo.resetState != util::UNSET_VALUE) { - mResetState = stateAtomInfo.resetState; - } +StateTracker::StateTracker(const int32_t atomId) : mField(atomId, 0) { } void StateTracker::onLogEvent(const LogEvent& event) { - int64_t eventTimeNs = event.GetElapsedTimestampNs(); + const int64_t eventTimeNs = event.GetElapsedTimestampNs(); // Parse event for primary field values i.e. primary key. HashableDimensionKey primaryKey; - if (mPrimaryFields.size() > 0) { - if (!filterValues(mPrimaryFields, event.getValues(), &primaryKey) || - primaryKey.getValues().size() != mPrimaryFields.size()) { - ALOGE("StateTracker error extracting primary key from log event."); - handleReset(eventTimeNs); - return; - } - } else { - // Use an empty HashableDimensionKey if atom has no primary fields. - primaryKey = DEFAULT_DIMENSION_KEY; + filterPrimaryKey(event.getValues(), &primaryKey); + + FieldValue stateValue; + if (!getStateFieldValueFromLogEvent(event, &stateValue)) { + ALOGE("StateTracker error extracting state from log event. Missing exclusive state field."); + clearStateForPrimaryKey(eventTimeNs, primaryKey); + return; } - // Parse event for state value. - FieldValue stateValue; - if (!filterValues(mStateField, event.getValues(), &stateValue) || - stateValue.mValue.getType() != INT) { + mField.setField(stateValue.mField.getField()); + + if (stateValue.mValue.getType() != INT) { ALOGE("StateTracker error extracting state from log event. Type: %d", stateValue.mValue.getType()); - handlePartialReset(eventTimeNs, primaryKey); + clearStateForPrimaryKey(eventTimeNs, primaryKey); return; } - int32_t state = stateValue.mValue.int_value; - if (state == mResetState) { - VLOG("StateTracker Reset state: %s", stateValue.mValue.toString().c_str()); - handleReset(eventTimeNs); + const int32_t resetState = stateValue.mAnnotations.getResetState(); + if (resetState != -1) { + VLOG("StateTracker new reset state: %d", resetState); + handleReset(eventTimeNs, resetState); return; } - // Track and update state. - int32_t oldState = 0; - int32_t newState = 0; - updateState(primaryKey, state, &oldState, &newState); - - // Notify all listeners if state has changed. - if (oldState != newState) { - VLOG("StateTracker updated state"); - for (auto listener : mListeners) { - auto sListener = listener.promote(); // safe access to wp<> - if (sListener != nullptr) { - sListener->onStateChanged(eventTimeNs, mAtomId, primaryKey, oldState, newState); - } - } - } else { - VLOG("StateTracker NO updated state"); - } + const int32_t newState = stateValue.mValue.int_value; + const bool nested = stateValue.mAnnotations.isNested(); + StateValueInfo* stateValueInfo = &mStateMap[primaryKey]; + updateStateForPrimaryKey(eventTimeNs, primaryKey, newState, nested, stateValueInfo); } void StateTracker::registerListener(wp listener) { @@ -111,81 +73,62 @@ void StateTracker::unregisterListener(wp listener) { } bool StateTracker::getStateValue(const HashableDimensionKey& queryKey, FieldValue* output) const { - output->mField = mStateField.mMatcher; + output->mField = mField; - // Check that the query key has the correct number of primary fields. - if (queryKey.getValues().size() == mPrimaryFields.size()) { - auto it = mStateMap.find(queryKey); - if (it != mStateMap.end()) { - output->mValue = it->second.state; - return true; - } - } else if (queryKey.getValues().size() > mPrimaryFields.size()) { - ALOGE("StateTracker query key size %zu > primary key size %zu is illegal", - queryKey.getValues().size(), mPrimaryFields.size()); - } else { - ALOGE("StateTracker query key size %zu < primary key size %zu is not supported", - queryKey.getValues().size(), mPrimaryFields.size()); + if (const auto it = mStateMap.find(queryKey); it != mStateMap.end()) { + output->mValue = it->second.state; + return true; } - // Set the state value to default state if: - // - query key size is incorrect - // - query key is not found in state map - output->mValue = mDefaultState; + // 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) { +void StateTracker::handleReset(const int64_t eventTimeNs, const int32_t newState) { VLOG("StateTracker handle reset"); - for (const auto pair : mStateMap) { - for (auto l : mListeners) { - auto sl = l.promote(); - if (sl != nullptr) { - sl->onStateChanged(eventTimeNs, mAtomId, pair.first, pair.second.state, - mDefaultState); - } - } - } - mStateMap.clear(); -} - -void StateTracker::handlePartialReset(const int64_t eventTimeNs, - const HashableDimensionKey& primaryKey) { - VLOG("StateTracker handle partial reset"); - if (mStateMap.find(primaryKey) != mStateMap.end()) { - for (auto l : mListeners) { - auto sl = l.promote(); - if (sl != nullptr) { - sl->onStateChanged(eventTimeNs, mAtomId, primaryKey, - mStateMap.find(primaryKey)->second.state, mDefaultState); - } - } - mStateMap.erase(primaryKey); + for (auto& [primaryKey, stateValueInfo] : mStateMap) { + updateStateForPrimaryKey(eventTimeNs, primaryKey, newState, + false /* nested; treat this state change as not nested */, + &stateValueInfo); } } -void StateTracker::updateState(const HashableDimensionKey& primaryKey, const int32_t eventState, - int32_t* oldState, int32_t* newState) { - // get old state (either current state in map or default state) - auto it = mStateMap.find(primaryKey); +void StateTracker::clearStateForPrimaryKey(const int64_t eventTimeNs, + const HashableDimensionKey& primaryKey) { + VLOG("StateTracker clear state for primary key"); + const std::unordered_map::iterator it = + mStateMap.find(primaryKey); + + // If there is no entry for the primaryKey in mStateMap, then the state is already + // kStateUnknown. if (it != mStateMap.end()) { - *oldState = it->second.state; - } else { - *oldState = mDefaultState; + updateStateForPrimaryKey(eventTimeNs, primaryKey, kStateUnknown, + false /* nested; treat this state change as not nested */, + &it->second); + } +} + +void StateTracker::updateStateForPrimaryKey(const int64_t eventTimeNs, + const HashableDimensionKey& primaryKey, + const int32_t newState, const bool nested, + StateValueInfo* stateValueInfo) { + const int32_t oldState = stateValueInfo->state; + + if (kStateUnknown == newState) { + mStateMap.erase(primaryKey); } // Update state map for non-nested counting case. // Every state event triggers a state overwrite. - if (!mNested) { - if (eventState == mDefaultState) { - // remove (key, state) pair if state returns to default state - VLOG("\t StateTracker changed to default state") - mStateMap.erase(primaryKey); - } else { - mStateMap[primaryKey].state = eventState; - mStateMap[primaryKey].count = 1; + if (!nested) { + stateValueInfo->state = newState; + stateValueInfo->count = 1; + + // Notify listeners if state has changed. + if (oldState != newState) { + notifyListeners(eventTimeNs, primaryKey, oldState, newState); } - *newState = eventState; return; } @@ -197,31 +140,47 @@ void StateTracker::updateState(const HashableDimensionKey& primaryKey, const int // number of OFF events as ON events. // // In atoms.proto, a state atom with nested counting enabled - // must only have 2 states and one of the states must be the default state. - it = mStateMap.find(primaryKey); - if (it != mStateMap.end()) { - *newState = it->second.state; - if (eventState == it->second.state) { - it->second.count++; - } else if (eventState == mDefaultState) { - if ((--it->second.count) == 0) { - mStateMap.erase(primaryKey); - *newState = mDefaultState; - } - } else { - ALOGE("StateTracker Nest counting state has a third state instead of the binary state " - "limit."); - return; + // must only have 2 states. There is no enforcemnt here of this requirement. + // The atom must be logged correctly. + if (kStateUnknown == newState) { + if (kStateUnknown != oldState) { + notifyListeners(eventTimeNs, primaryKey, oldState, newState); } - } else { - if (eventState != mDefaultState) { - mStateMap[primaryKey].state = eventState; - mStateMap[primaryKey].count = 1; - } - *newState = eventState; + } else if (oldState == kStateUnknown) { + stateValueInfo->state = newState; + stateValueInfo->count = 1; + notifyListeners(eventTimeNs, primaryKey, oldState, newState); + } else if (oldState == newState) { + stateValueInfo->count++; + } else if (--stateValueInfo->count == 0) { + stateValueInfo->state = newState; + stateValueInfo->count = 1; + notifyListeners(eventTimeNs, primaryKey, oldState, newState); } } +void StateTracker::notifyListeners(const int64_t eventTimeNs, + const HashableDimensionKey& primaryKey, const int32_t oldState, + const int32_t 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 index 154750e6da3d0..c5f6315fc9921 100644 --- a/cmds/statsd/src/state/StateTracker.h +++ b/cmds/statsd/src/state/StateTracker.h @@ -15,7 +15,6 @@ */ #pragma once -#include #include #include "HashableDimensionKey.h" #include "logd/LogEvent.h" @@ -30,7 +29,7 @@ namespace statsd { class StateTracker : public virtual RefBase { public: - StateTracker(const int32_t atomId, const android::util::StateAtomFieldOptions& stateAtomInfo); + StateTracker(const int32_t atomId); virtual ~StateTracker(){}; @@ -60,21 +59,11 @@ public: private: struct StateValueInfo { - int32_t state; // state value - int count; // nested count (only used for binary states) + int32_t state = kStateUnknown; // state value + int count = 0; // nested count (only used for binary states) }; - const int32_t mAtomId; // id of the state atom being tracked - - Matcher mStateField; // matches the atom's exclusive state field - - std::vector mPrimaryFields; // matches the atom's primary fields - - int32_t mDefaultState = kStateUnknown; - - int32_t mResetState = kStateUnknown; - - const bool mNested; + Field mField; // Maps primary key to state value info std::unordered_map mStateMap; @@ -82,20 +71,24 @@ private: // Set of all StateListeners (objects listening for state changes) std::set> mListeners; - // Reset all state values in map to default state. - void handleReset(const int64_t eventTimeNs); + // Reset all state values in map to the given state. + void handleReset(const int64_t eventTimeNs, const int32_t newState); - // Reset only the state value mapped to the given primary key to default state. - // Partial resets are used when we only need to update the state of one primary - // key instead of clearing/reseting every key in the map. - void handlePartialReset(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey); + // 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. - // Store the old and new states. - void updateState(const HashableDimensionKey& primaryKey, const int32_t eventState, - int32_t* oldState, int32_t* newState); + void updateStateForPrimaryKey(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey, + const int32_t newState, const bool nested, + StateValueInfo* stateValueInfo); + + // Notify registered state listeners of state change. + void notifyListeners(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey, + const int32_t oldState, const int32_t newState); }; +bool getStateFieldValueFromLogEvent(const LogEvent& event, FieldValue* output); + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp index a5da9c8a6f56a..b1461a1535ba1 100644 --- a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp @@ -232,7 +232,7 @@ TEST(CountMetricE2eTest, TestSlicedStateWithMap) { StateMap map = state.map(); for (auto group : map.group()) { for (auto value : group.value()) { - EXPECT_EQ(metricProducer->mStateGroupMap[SCREEN_STATE_ATOM_ID][value], + EXPECT_EQ(metricProducer->mStateGroupMap.at(SCREEN_STATE_ATOM_ID).at(value), group.group_id()); } } @@ -614,7 +614,7 @@ TEST(CountMetricE2eTest, TestMultipleSlicedStates) { StateMap map = state1.map(); for (auto group : map.group()) { for (auto value : group.value()) { - EXPECT_EQ(metricProducer->mStateGroupMap[SCREEN_STATE_ATOM_ID][value], + EXPECT_EQ(metricProducer->mStateGroupMap.at(SCREEN_STATE_ATOM_ID).at(value), group.group_id()); } } diff --git a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp index ba09a353e8b62..d59ec3ef4a29a 100644 --- a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp @@ -921,18 +921,18 @@ TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState) { bucket #1 bucket #2 | 1 2 3 4 5 6 7 8 (minutes) |---------------------------------------|------------------ - ON OFF ON (BatterySaverMode) + ON OFF ON (BatterySaverMode) T F T (DeviceUnpluggedPredicate) - | | | (ScreenIsOnEvent) + | | | (ScreenIsOnEvent) | | | (ScreenIsOffEvent) | (ScreenDozeEvent) */ // Initialize log events. std::vector> events; - events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 20 * NS_PER_SEC)); // 0:30 events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 60 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 1:10 + bucketStartTimeNs + 20 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 0:30 + events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 60 * NS_PER_SEC)); // 1:10 events.push_back(CreateScreenStateChangedEvent( bucketStartTimeNs + 80 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:30 diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp index 78c80bc8307ce..ba2a4cfbe8fde 100644 --- a/cmds/statsd/tests/state/StateTracker_test.cpp +++ b/cmds/statsd/tests/state/StateTracker_test.cpp @@ -19,6 +19,7 @@ #include "state/StateListener.h" #include "state/StateManager.h" +#include "state/StateTracker.h" #include "stats_event.h" #include "tests/statsd_test_util.h" @@ -127,23 +128,23 @@ TEST(StateTrackerTest, TestRegisterListener) { // Register listener to non-existing StateTracker EXPECT_EQ(0, mgr.getStateTrackersCount()); - EXPECT_TRUE(mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1)); + mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1); EXPECT_EQ(1, mgr.getStateTrackersCount()); EXPECT_EQ(1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED)); // Register listener to existing StateTracker - EXPECT_TRUE(mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2)); + mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2); EXPECT_EQ(1, mgr.getStateTrackersCount()); EXPECT_EQ(2, mgr.getListenersCount(util::SCREEN_STATE_CHANGED)); // Register already registered listener to existing StateTracker - EXPECT_TRUE(mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2)); + mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2); EXPECT_EQ(1, mgr.getStateTrackersCount()); EXPECT_EQ(2, mgr.getListenersCount(util::SCREEN_STATE_CHANGED)); // Register listener to non-state atom - EXPECT_FALSE(mgr.registerListener(util::BATTERY_LEVEL_CHANGED, listener2)); - EXPECT_EQ(1, mgr.getStateTrackersCount()); + mgr.registerListener(util::BATTERY_LEVEL_CHANGED, listener2); + EXPECT_EQ(2, mgr.getStateTrackersCount()); } /** @@ -249,6 +250,9 @@ TEST(StateTrackerTest, TestStateChangeReset) { EXPECT_EQ(1, listener->updates.size()); EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value); EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState); + FieldValue stateFieldValue; + mgr.getStateValue(util::BLE_SCAN_STATE_CHANGED, listener->updates[0].mKey, &stateFieldValue); + EXPECT_EQ(BleScanStateChanged::ON, stateFieldValue.mValue.int_value); listener->updates.clear(); std::unique_ptr event2 = @@ -258,6 +262,8 @@ TEST(StateTrackerTest, TestStateChangeReset) { EXPECT_EQ(1, listener->updates.size()); EXPECT_EQ(2000, listener->updates[0].mKey.getValues()[0].mValue.int_value); EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState); + mgr.getStateValue(util::BLE_SCAN_STATE_CHANGED, listener->updates[0].mKey, &stateFieldValue); + EXPECT_EQ(BleScanStateChanged::ON, stateFieldValue.mValue.int_value); listener->updates.clear(); std::unique_ptr event3 = @@ -265,8 +271,12 @@ TEST(StateTrackerTest, TestStateChangeReset) { BleScanStateChanged::RESET, false, false, false); mgr.onLogEvent(*event3); EXPECT_EQ(2, listener->updates.size()); - EXPECT_EQ(BleScanStateChanged::OFF, listener->updates[0].mState); - EXPECT_EQ(BleScanStateChanged::OFF, listener->updates[1].mState); + for (const TestStateListener::Update& update : listener->updates) { + EXPECT_EQ(BleScanStateChanged::OFF, update.mState); + + mgr.getStateValue(util::BLE_SCAN_STATE_CHANGED, update.mKey, &stateFieldValue); + EXPECT_EQ(BleScanStateChanged::OFF, stateFieldValue.mValue.int_value); + } } /** @@ -352,13 +362,13 @@ TEST(StateTrackerTest, TestStateChangePrimaryFieldAttrChain) { // No state stored for this query key. HashableDimensionKey queryKey2; getPartialWakelockKey(1002 /* uid */, "tag1", &queryKey2); - EXPECT_EQ(WakelockStateChanged::RELEASE, + EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey2)); // Partial query fails. HashableDimensionKey queryKey3; getPartialWakelockKey(1001 /* uid */, &queryKey3); - EXPECT_EQ(WakelockStateChanged::RELEASE, + EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey3)); } diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index 687014f6a2986..7216e1d8cc8e2 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -569,6 +569,8 @@ std::unique_ptr CreateScreenStateChangedEvent( AStatsEvent_setAtomId(statsEvent, util::SCREEN_STATE_CHANGED); AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); AStatsEvent_writeInt32(statsEvent, state); + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true); + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false); std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); parseStatsEventToLogEvent(statsEvent, logEvent.get()); @@ -662,9 +664,14 @@ std::unique_ptr CreateWakelockStateChangedEvent(uint64_t timestampNs, AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); writeAttribution(statsEvent, attributionUids, attributionTags); + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true); AStatsEvent_writeInt32(statsEvent, android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK); + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true); AStatsEvent_writeString(statsEvent, wakelockName.c_str()); + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true); AStatsEvent_writeInt32(statsEvent, state); + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true); + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, true); std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); parseStatsEventToLogEvent(statsEvent, logEvent.get()); @@ -803,7 +810,11 @@ std::unique_ptr CreateUidProcessStateChangedEvent( AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); AStatsEvent_writeInt32(statsEvent, uid); + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_IS_UID, true); + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true); AStatsEvent_writeInt32(statsEvent, state); + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true); + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false); std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); parseStatsEventToLogEvent(statsEvent, logEvent.get()); @@ -821,10 +832,20 @@ std::unique_ptr CreateBleScanStateChangedEvent(uint64_t timestampNs, AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); writeAttribution(statsEvent, attributionUids, attributionTags); + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true); AStatsEvent_writeInt32(statsEvent, state); - AStatsEvent_writeInt32(statsEvent, filtered); // filtered - AStatsEvent_writeInt32(statsEvent, firstMatch); // first match - AStatsEvent_writeInt32(statsEvent, opportunistic); // opportunistic + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true); + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, true); + if (state == util::BLE_SCAN_STATE_CHANGED__STATE__RESET) { + AStatsEvent_addInt32Annotation(statsEvent, ANNOTATION_ID_TRIGGER_STATE_RESET, + util::BLE_SCAN_STATE_CHANGED__STATE__OFF); + } + AStatsEvent_writeBool(statsEvent, filtered); // filtered + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true); + AStatsEvent_writeBool(statsEvent, firstMatch); // first match + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true); + AStatsEvent_writeBool(statsEvent, opportunistic); // opportunistic + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true); std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); parseStatsEventToLogEvent(statsEvent, logEvent.get()); @@ -840,9 +861,14 @@ std::unique_ptr CreateOverlayStateChangedEvent(int64_t timestampNs, co AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); AStatsEvent_writeInt32(statsEvent, uid); + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_IS_UID, true); + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true); AStatsEvent_writeString(statsEvent, packageName.c_str()); - AStatsEvent_writeInt32(statsEvent, usingAlertWindow); + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true); + AStatsEvent_writeBool(statsEvent, usingAlertWindow); AStatsEvent_writeInt32(statsEvent, state); + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true); + AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false); std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); parseStatsEventToLogEvent(statsEvent, logEvent.get()); diff --git a/tools/stats_log_api_gen/atoms_info_writer.cpp b/tools/stats_log_api_gen/atoms_info_writer.cpp index 5fe94987aa65a..9261da64f2d54 100644 --- a/tools/stats_log_api_gen/atoms_info_writer.cpp +++ b/tools/stats_log_api_gen/atoms_info_writer.cpp @@ -26,26 +26,11 @@ namespace android { namespace stats_log_api_gen { static void write_atoms_info_header_body(FILE* out, const Atoms& atoms) { - fprintf(out, "static int UNSET_VALUE = INT_MAX;\n"); - fprintf(out, "static int FIRST_UID_IN_CHAIN = 0;\n"); - - fprintf(out, "struct StateAtomFieldOptions {\n"); - fprintf(out, " std::vector primaryFields;\n"); - fprintf(out, " int exclusiveField;\n"); - fprintf(out, " int defaultState = UNSET_VALUE;\n"); - fprintf(out, " int resetState = UNSET_VALUE;\n"); - fprintf(out, " bool nested;\n"); - fprintf(out, "};\n"); - fprintf(out, "\n"); - fprintf(out, "struct AtomsInfo {\n"); fprintf(out, " const static std::set " "kTruncatingTimestampAtomBlackList;\n"); fprintf(out, " const static std::set kAtomsWithAttributionChain;\n"); - fprintf(out, - " const static std::map " - "kStateAtomsFieldOptions;\n"); fprintf(out, " const static std::set kWhitelistedAtoms;\n"); fprintf(out, "};\n"); fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n", atoms.maxPushedAtomId); @@ -100,49 +85,6 @@ static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) { fprintf(out, "};\n"); fprintf(out, "\n"); - fprintf(out, - "static std::map " - "getStateAtomFieldOptions() {\n"); - fprintf(out, " std::map options;\n"); - fprintf(out, " StateAtomFieldOptions* opt;\n"); - for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end(); - atomIt++) { - if ((*atomIt)->primaryFields.size() == 0 && (*atomIt)->exclusiveField == 0) { - continue; - } - fprintf(out, - "\n // Adding primary and exclusive fields for atom " - "(%d)%s\n", - (*atomIt)->code, (*atomIt)->name.c_str()); - fprintf(out, " opt = &(options[%d /* %s */]);\n", (*atomIt)->code, - make_constant_name((*atomIt)->name).c_str()); - fprintf(out, " opt->primaryFields.reserve(%lu);\n", (*atomIt)->primaryFields.size()); - for (const auto& field : (*atomIt)->primaryFields) { - fprintf(out, " opt->primaryFields.push_back(%d);\n", field); - } - - fprintf(out, " opt->exclusiveField = %d;\n", (*atomIt)->exclusiveField); - if ((*atomIt)->defaultState != INT_MAX) { - fprintf(out, " opt->defaultState = %d;\n", (*atomIt)->defaultState); - } else { - fprintf(out, " opt->defaultState = UNSET_VALUE;\n"); - } - - if ((*atomIt)->triggerStateReset != INT_MAX) { - fprintf(out, " opt->resetState = %d;\n", (*atomIt)->triggerStateReset); - } else { - fprintf(out, " opt->resetState = UNSET_VALUE;\n"); - } - fprintf(out, " opt->nested = %d;\n", (*atomIt)->nested); - } - - fprintf(out, " return options;\n"); - fprintf(out, "}\n"); - - fprintf(out, - "const std::map " - "AtomsInfo::kStateAtomsFieldOptions = " - "getStateAtomFieldOptions();\n"); } int write_atoms_info_header(FILE* out, const Atoms& atoms, const string& namespaceStr) {