Merge "Remove kStateAtomsInfo from atoms_info." into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
1f976bf634
@@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -180,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) {
|
||||
|
||||
@@ -153,6 +153,18 @@ bool filterValues(const Matcher& matcherField, const std::vector<FieldValue>& va
|
||||
bool filterValues(const std::vector<Matcher>& matcherFields, const std::vector<FieldValue>& 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<FieldValue>& values, HashableDimensionKey* output);
|
||||
|
||||
/**
|
||||
* Filter the values from FieldValues using the matchers.
|
||||
*
|
||||
|
||||
@@ -293,7 +293,8 @@ void LogEvent::parseExclusiveStateAnnotation(uint8_t annotationType) {
|
||||
}
|
||||
|
||||
const bool exclusiveState = readNextValue<uint8_t>();
|
||||
mValues[mValues.size() - 1].mAnnotations.setExclusiveState(exclusiveState);
|
||||
mExclusiveStateFieldIndex = mValues.size() - 1;
|
||||
mValues[getExclusiveStateFieldIndex()].mAnnotations.setExclusiveState(exclusiveState);
|
||||
}
|
||||
|
||||
void LogEvent::parseTriggerStateResetAnnotation(uint8_t annotationType) {
|
||||
|
||||
@@ -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<int64_t>& experimentIds, std::vector<uint8_t>* protoOut);
|
||||
|
||||
@@ -451,7 +451,7 @@ protected:
|
||||
std::vector<int32_t> mSlicedStateAtoms;
|
||||
|
||||
// Maps atom ids and state values to group_ids (<atom_id, <value, group_id>>).
|
||||
std::unordered_map<int32_t, std::unordered_map<int, int64_t>> mStateGroupMap;
|
||||
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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -38,20 +38,12 @@ void StateManager::onLogEvent(const LogEvent& event) {
|
||||
}
|
||||
}
|
||||
|
||||
bool StateManager::registerListener(const int32_t atomId, wp<StateListener> listener) {
|
||||
void StateManager::registerListener(const int32_t atomId, wp<StateListener> 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<StateListener> listener) {
|
||||
|
||||
@@ -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<StateListener> 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<StateListener> listener);
|
||||
|
||||
// Notifies the correct StateTracker to unregister a listener
|
||||
// and removes the tracker if it no longer has any listeners.
|
||||
|
||||
@@ -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<StateListener> listener) {
|
||||
@@ -111,81 +73,62 @@ void StateTracker::unregisterListener(wp<StateListener> 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<HashableDimensionKey, StateValueInfo>::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
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <atoms_info.h>
|
||||
#include <utils/RefBase.h>
|
||||
#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<Matcher> 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<HashableDimensionKey, StateValueInfo> mStateMap;
|
||||
@@ -82,20 +71,24 @@ private:
|
||||
// Set of all StateListeners (objects listening for state changes)
|
||||
std::set<wp<StateListener>> 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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<std::unique_ptr<LogEvent>> 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
|
||||
|
||||
@@ -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<LogEvent> 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<LogEvent> 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));
|
||||
}
|
||||
|
||||
|
||||
@@ -569,6 +569,8 @@ std::unique_ptr<LogEvent> 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> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
|
||||
parseStatsEventToLogEvent(statsEvent, logEvent.get());
|
||||
@@ -662,9 +664,14 @@ std::unique_ptr<LogEvent> 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> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
|
||||
parseStatsEventToLogEvent(statsEvent, logEvent.get());
|
||||
@@ -803,7 +810,11 @@ std::unique_ptr<LogEvent> 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> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
|
||||
parseStatsEventToLogEvent(statsEvent, logEvent.get());
|
||||
@@ -821,10 +832,20 @@ std::unique_ptr<LogEvent> 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> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
|
||||
parseStatsEventToLogEvent(statsEvent, logEvent.get());
|
||||
@@ -840,9 +861,14 @@ std::unique_ptr<LogEvent> 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> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
|
||||
parseStatsEventToLogEvent(statsEvent, logEvent.get());
|
||||
|
||||
@@ -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<int> 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<int> "
|
||||
"kTruncatingTimestampAtomBlackList;\n");
|
||||
fprintf(out, " const static std::set<int> kAtomsWithAttributionChain;\n");
|
||||
fprintf(out,
|
||||
" const static std::map<int, StateAtomFieldOptions> "
|
||||
"kStateAtomsFieldOptions;\n");
|
||||
fprintf(out, " const static std::set<int> 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<int, StateAtomFieldOptions> "
|
||||
"getStateAtomFieldOptions() {\n");
|
||||
fprintf(out, " std::map<int, StateAtomFieldOptions> 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<int, StateAtomFieldOptions> "
|
||||
"AtomsInfo::kStateAtomsFieldOptions = "
|
||||
"getStateAtomFieldOptions();\n");
|
||||
}
|
||||
|
||||
int write_atoms_info_header(FILE* out, const Atoms& atoms, const string& namespaceStr) {
|
||||
|
||||
Reference in New Issue
Block a user