Merge "Allow default state, reset state, and nesting for binary states"

This commit is contained in:
Christine Tsai
2020-02-03 22:46:06 +00:00
committed by Android (Google) Code Review
9 changed files with 314 additions and 104 deletions

View File

@@ -24,46 +24,75 @@ option java_outer_classname = "AtomFieldOptions";
import "google/protobuf/descriptor.proto";
enum StateField {
// Default value for fields that are not primary or exclusive state.
// Default value for fields that are not a primary or exclusive state field.
STATE_FIELD_UNSET = 0;
// Fields that represent the key that the state belongs to.
PRIMARY = 1;
// Used on simple proto fields. Do not use on attribution chains.
PRIMARY_FIELD = 1;
// The field that represents the state. It's an exclusive state.
EXCLUSIVE = 2;
EXCLUSIVE_STATE = 2;
// Used on an attribution chain field to indicate that the first uid is the
// primary field.
PRIMARY_FIELD_FIRST_UID = 3;
}
// 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).option = PRIMARY_FIELD];
// optional android.app.ProcessStateEnum state = 2 [(state_field_option).option =
// EXCLUSIVE_STATE];
// }
// 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).option = PRIMARY_FIELD];
// optional int32 tid = 2 [(state_field_option).option = PRIMARY_FIELD];
// optional int32 state = 3 [(state_field_option).option = EXCLUSIVE_STATE];
// }
//
// 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).option =
// EXCLUSIVE_STATE];
// }
//
// 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).option = PRIMARY_KEY_FIRST_UID];
//
// 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).option = EXCLUSIVE_STATE];
// }
message StateAtomFieldOption {
optional StateField option = 1 [default = STATE_FIELD_UNSET];
// 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 = 2;
// 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 reset_state_value = 3;
// If the state change needs to count nesting.
optional bool nested = 4 [default = true];
}
// Used to generate StatsLog.write APIs.

View File

@@ -563,7 +563,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).option = EXCLUSIVE_STATE, (state_field_option).nested = false];
}
/**
@@ -574,10 +575,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).option = PRIMARY_FIELD, (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).option = EXCLUSIVE_STATE, (state_field_option).nested = false];
}
/**
@@ -609,7 +611,7 @@ message ActivityManagerSleepStateChanged {
ASLEEP = 1;
AWAKE = 2;
}
optional State state = 1 [(state_field_option).option = EXCLUSIVE];
optional State state = 1 [(state_field_option).option = EXCLUSIVE_STATE];
}
/**
@@ -628,7 +630,7 @@ message MemoryFactorStateChanged {
CRITICAL = 4; // critical memory.
}
optional State factor = 1 [(state_field_option).option = EXCLUSIVE];
optional State factor = 1 [(state_field_option).option = EXCLUSIVE_STATE];
}
/**
@@ -790,7 +792,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).option = PRIMARY_FIELD_FIRST_UID];
enum State {
OFF = 0;
@@ -798,14 +801,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).option = EXCLUSIVE_STATE,
(state_field_option).default_state_value = 0 /* State.OFF */,
(state_field_option).reset_state_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).option = PRIMARY_FIELD];
// 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).option = PRIMARY_FIELD];
// 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).option = PRIMARY_FIELD];
}
/**
@@ -1029,11 +1037,11 @@ message WakelockStateChanged {
// 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 [(state_field_option).option = PRIMARY];
optional android.os.WakeLockLevelEnum type = 2 [(state_field_option).option = PRIMARY_FIELD];
;
// The wakelock tag (Called tag in the Java API, sometimes name elsewhere).
optional string tag = 3 [(state_field_option).option = PRIMARY];
optional string tag = 3 [(state_field_option).option = PRIMARY_FIELD];
enum State {
RELEASE = 0;
@@ -1041,7 +1049,11 @@ message WakelockStateChanged {
CHANGE_RELEASE = 2;
CHANGE_ACQUIRE = 3;
}
optional State state = 4 [(state_field_option).option = EXCLUSIVE];
optional State state = 4 [
(state_field_option).option = EXCLUSIVE_STATE,
(state_field_option).default_state_value = 0,
(state_field_option).nested = true
];
}
/**
@@ -3225,9 +3237,9 @@ message PictureInPictureStateChanged {
* services/core/java/com/android/server/wm/Session.java
*/
message OverlayStateChanged {
optional int32 uid = 1 [(state_field_option).option = PRIMARY, (is_uid) = true];
optional int32 uid = 1 [(state_field_option).option = PRIMARY_FIELD, (is_uid) = true];
optional string package_name = 2 [(state_field_option).option = PRIMARY];
optional string package_name = 2 [(state_field_option).option = PRIMARY_FIELD];
optional bool using_alert_window = 3;
@@ -3235,7 +3247,11 @@ message OverlayStateChanged {
ENTERED = 1;
EXITED = 2;
}
optional State state = 4 [(state_field_option).option = EXCLUSIVE];
optional State state = 4 [
(state_field_option).option = EXCLUSIVE_STATE,
(state_field_option).nested = false,
(state_field_option).default_state_value = 2
];
}
/*
@@ -3401,7 +3417,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).option = EXCLUSIVE_STATE];
}
/**
@@ -3921,7 +3937,7 @@ message PrivacyIndicatorsInteracted {
DIALOG_LINE_ITEM = 5;
}
optional Type type = 1 [(state_field_option).option = EXCLUSIVE];
optional Type type = 1 [(state_field_option).option = EXCLUSIVE_STATE];
// Used if the type is LINE_ITEM
optional string package_name = 2;

View File

@@ -26,7 +26,9 @@ namespace os {
namespace statsd {
StateTracker::StateTracker(const int32_t atomId, const util::StateAtomFieldOptions& stateAtomInfo)
: mAtomId(atomId), mStateField(getSimpleMatcher(atomId, stateAtomInfo.exclusiveField)) {
: 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) {
@@ -38,7 +40,13 @@ StateTracker::StateTracker(const int32_t atomId, const util::StateAtomFieldOptio
}
}
// TODO(tsaichristine): b/142108433 set default state, reset state, and nesting
if (stateAtomInfo.defaultState != util::UNSET_VALUE) {
mDefaultState = stateAtomInfo.defaultState;
}
if (stateAtomInfo.resetState != util::UNSET_VALUE) {
mResetState = stateAtomInfo.resetState;
}
}
void StateTracker::onLogEvent(const LogEvent& event) {
@@ -60,7 +68,6 @@ void StateTracker::onLogEvent(const LogEvent& event) {
// Parse event for state value.
FieldValue stateValue;
int32_t state;
if (!filterValues(mStateField, event.getValues(), &stateValue) ||
stateValue.mValue.getType() != INT) {
ALOGE("StateTracker error extracting state from log event. Type: %d",
@@ -68,11 +75,12 @@ void StateTracker::onLogEvent(const LogEvent& event) {
handlePartialReset(eventTimeNs, primaryKey);
return;
}
state = stateValue.mValue.int_value;
int32_t state = stateValue.mValue.int_value;
if (state == mResetState) {
VLOG("StateTracker Reset state: %s", stateValue.mValue.toString().c_str());
handleReset(eventTimeNs);
return;
}
// Track and update state.
@@ -113,15 +121,17 @@ bool StateTracker::getStateValue(const HashableDimensionKey& queryKey, FieldValu
return true;
}
} else if (queryKey.getValues().size() > mPrimaryFields.size()) {
ALOGE("StateTracker query key size > primary key size is illegal");
ALOGE("StateTracker query key size %zu > primary key size %zu is illegal",
queryKey.getValues().size(), mPrimaryFields.size());
} else {
ALOGE("StateTracker query key size < primary key size is not supported");
ALOGE("StateTracker query key size %zu < primary key size %zu is not supported",
queryKey.getValues().size(), mPrimaryFields.size());
}
// Set the state value to unknown if:
// Set the state value to default state if:
// - query key size is incorrect
// - query key is not found in state map
output->mValue = StateTracker::kStateUnknown;
output->mValue = mDefaultState;
return false;
}
@@ -164,18 +174,52 @@ void StateTracker::updateState(const HashableDimensionKey& primaryKey, const int
*oldState = mDefaultState;
}
// update state map
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;
// 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;
}
*newState = eventState;
return;
}
*newState = eventState;
// TODO(tsaichristine): support atoms with nested counting
// 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 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;
}
} else {
if (eventState != mDefaultState) {
mStateMap[primaryKey].state = eventState;
mStateMap[primaryKey].count = 1;
}
*newState = eventState;
}
}
} // namespace statsd

View File

@@ -74,6 +74,8 @@ private:
int32_t mResetState = kStateUnknown;
const bool mNested;
// Maps primary key to state value info
std::unordered_map<HashableDimensionKey, StateValueInfo> mStateMap;

View File

@@ -127,6 +127,23 @@ std::shared_ptr<LogEvent> buildOverlayEventBadStateType(int uid, const std::stri
event->init();
return event;
}
std::shared_ptr<LogEvent> buildBleScanEvent(int uid, bool acquire, bool reset) {
std::vector<AttributionNodeInternal> chain;
chain.push_back(AttributionNodeInternal());
AttributionNodeInternal& attr = chain.back();
attr.set_uid(uid);
std::shared_ptr<LogEvent> event =
std::make_shared<LogEvent>(android::util::BLE_SCAN_STATE_CHANGED, 1000);
event->write(chain);
event->write(reset ? 2 : acquire ? 1 : 0); // PARTIAL_WAKE_LOCK
event->write(0); // filtered
event->write(0); // first match
event->write(0); // opportunistic
event->init();
return event;
}
// END: build event functions.
// START: get primary key functions
@@ -276,6 +293,80 @@ TEST(StateTrackerTest, TestUnregisterListener) {
EXPECT_EQ(-1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
}
/**
* Test a binary state atom with nested counting.
*
* To go from an "ON" state to an "OFF" state with nested counting, we must see
* an equal number of "OFF" events as "ON" events.
* For example, ACQUIRE, ACQUIRE, RELEASE will still be in the ACQUIRE state.
* ACQUIRE, ACQUIRE, RELEASE, RELEASE will be in the RELEASE state.
*/
TEST(StateTrackerTest, TestStateChangeNested) {
sp<TestStateListener> listener = new TestStateListener();
StateManager mgr;
mgr.registerListener(android::util::WAKELOCK_STATE_CHANGED, listener);
std::shared_ptr<LogEvent> event1 =
buildPartialWakelockEvent(1000 /* uid */, "tag", true /*acquire*/);
mgr.onLogEvent(*event1);
EXPECT_EQ(1, listener->updates.size());
EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
EXPECT_EQ(1, listener->updates[0].mState);
listener->updates.clear();
std::shared_ptr<LogEvent> event2 =
buildPartialWakelockEvent(1000 /* uid */, "tag", true /*acquire*/);
mgr.onLogEvent(*event2);
EXPECT_EQ(0, listener->updates.size());
std::shared_ptr<LogEvent> event3 =
buildPartialWakelockEvent(1000 /* uid */, "tag", false /*release*/);
mgr.onLogEvent(*event3);
EXPECT_EQ(0, listener->updates.size());
std::shared_ptr<LogEvent> event4 =
buildPartialWakelockEvent(1000 /* uid */, "tag", false /*release*/);
mgr.onLogEvent(*event4);
EXPECT_EQ(1, listener->updates.size());
EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
EXPECT_EQ(0, listener->updates[0].mState);
}
/**
* Test a state atom with a reset state.
*
* If the reset state value is seen, every state in the map is set to the default
* state and every listener is notified.
*/
TEST(StateTrackerTest, TestStateChangeReset) {
sp<TestStateListener> listener = new TestStateListener();
StateManager mgr;
mgr.registerListener(android::util::BLE_SCAN_STATE_CHANGED, listener);
std::shared_ptr<LogEvent> event1 =
buildBleScanEvent(1000 /* uid */, true /*acquire*/, false /*reset*/);
mgr.onLogEvent(*event1);
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);
listener->updates.clear();
std::shared_ptr<LogEvent> event2 =
buildBleScanEvent(2000 /* uid */, true /*acquire*/, false /*reset*/);
mgr.onLogEvent(*event2);
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);
listener->updates.clear();
std::shared_ptr<LogEvent> event3 =
buildBleScanEvent(2000 /* uid */, false /*acquire*/, true /*reset*/);
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);
}
/**
* Test StateManager's onLogEvent and StateListener's onStateChanged correctly
* updates listener for states without primary keys.
@@ -334,7 +425,7 @@ TEST(StateTrackerTest, TestStateChangePrimaryFieldAttrChain) {
// Log event.
std::shared_ptr<LogEvent> event =
buildPartialWakelockEvent(1001 /* uid */, "tag1", false /* acquire */);
buildPartialWakelockEvent(1001 /* uid */, "tag1", true /* acquire */);
mgr.onLogEvent(*event);
EXPECT_EQ(1, mgr.getStateTrackersCount());
@@ -346,23 +437,25 @@ TEST(StateTrackerTest, TestStateChangePrimaryFieldAttrChain) {
EXPECT_EQ(1001, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
EXPECT_EQ(1, listener1->updates[0].mKey.getValues()[1].mValue.int_value);
EXPECT_EQ("tag1", listener1->updates[0].mKey.getValues()[2].mValue.str_value);
EXPECT_EQ(WakelockStateChanged::RELEASE, listener1->updates[0].mState);
EXPECT_EQ(WakelockStateChanged::ACQUIRE, listener1->updates[0].mState);
// Check StateTracker was updated by querying for state.
HashableDimensionKey queryKey;
getPartialWakelockKey(1001 /* uid */, "tag1", &queryKey);
EXPECT_EQ(WakelockStateChanged::RELEASE,
EXPECT_EQ(WakelockStateChanged::ACQUIRE,
getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey));
// No state stored for this query key.
HashableDimensionKey queryKey2;
getPartialWakelockKey(1002 /* uid */, "tag1", &queryKey2);
EXPECT_EQ(-1, getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey2));
EXPECT_EQ(WakelockStateChanged::RELEASE,
getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey2));
// Partial query fails.
HashableDimensionKey queryKey3;
getPartialWakelockKey(1001 /* uid */, &queryKey3);
EXPECT_EQ(-1, getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey3));
EXPECT_EQ(WakelockStateChanged::RELEASE,
getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey3));
}
/**

View File

@@ -40,18 +40,21 @@ AtomDecl::AtomDecl()
{
}
AtomDecl::AtomDecl(const AtomDecl& that)
: code(that.code),
name(that.name),
message(that.message),
fields(that.fields),
primaryFields(that.primaryFields),
exclusiveField(that.exclusiveField),
uidField(that.uidField),
whitelisted(that.whitelisted),
binaryFields(that.binaryFields),
hasModule(that.hasModule),
moduleName(that.moduleName) {}
AtomDecl::AtomDecl(const AtomDecl &that)
: code(that.code),
name(that.name),
message(that.message),
fields(that.fields),
primaryFields(that.primaryFields),
exclusiveField(that.exclusiveField),
defaultState(that.defaultState),
resetState(that.resetState),
nested(that.nested),
uidField(that.uidField),
whitelisted(that.whitelisted),
binaryFields(that.binaryFields),
hasModule(that.hasModule),
moduleName(that.moduleName) {}
AtomDecl::AtomDecl(int c, const string& n, const string& m)
:code(c),
@@ -281,7 +284,7 @@ int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
atomDecl->fields.push_back(atField);
if (field->options().GetExtension(os::statsd::state_field_option).option() ==
os::statsd::StateField::PRIMARY) {
os::statsd::StateField::PRIMARY_FIELD) {
if (javaType == JAVA_TYPE_UNKNOWN ||
javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
@@ -300,7 +303,7 @@ int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
}
if (field->options().GetExtension(os::statsd::state_field_option).option() ==
os::statsd::StateField::EXCLUSIVE) {
os::statsd::StateField::EXCLUSIVE_STATE) {
if (javaType == JAVA_TYPE_UNKNOWN ||
javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
@@ -312,6 +315,21 @@ int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
} else {
errorCount++;
}
if (field->options()
.GetExtension(os::statsd::state_field_option)
.has_default_state_value()) {
atomDecl->defaultState = field->options()
.GetExtension(os::statsd::state_field_option)
.default_state_value();
}
if (field->options().GetExtension(os::statsd::state_field_option).has_reset_state_value()) {
atomDecl->resetState = field->options()
.GetExtension(os::statsd::state_field_option)
.reset_state_value();
}
atomDecl->nested = field->options().GetExtension(os::statsd::state_field_option).nested();
}
if (field->options().GetExtension(os::statsd::is_uid) == true) {

View File

@@ -88,6 +88,9 @@ struct AtomDecl {
vector<int> primaryFields;
int exclusiveField = 0;
int defaultState = INT_MAX;
int resetState = INT_MAX;
bool nested;
int uidField = 0;

View File

@@ -25,11 +25,15 @@ 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");
@@ -126,7 +130,7 @@ static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) {
"static std::map<int, StateAtomFieldOptions> "
"getStateAtomFieldOptions() {\n");
fprintf(out, " std::map<int, StateAtomFieldOptions> options;\n");
fprintf(out, " StateAtomFieldOptions opt;\n");
fprintf(out, " StateAtomFieldOptions* opt;\n");
for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
atom != atoms.decls.end(); atom++) {
if (atom->primaryFields.size() == 0 && atom->exclusiveField == 0) {
@@ -136,14 +140,26 @@ static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) {
"\n // Adding primary and exclusive fields for atom "
"(%d)%s\n",
atom->code, atom->name.c_str());
fprintf(out, " opt.primaryFields.clear();\n");
fprintf(out, " opt = &(options[static_cast<int>(%s)]);\n",
make_constant_name(atom->name).c_str());
fprintf(out, " opt->primaryFields.reserve(%lu);\n", atom->primaryFields.size());
for (const auto& field : atom->primaryFields) {
fprintf(out, " opt.primaryFields.push_back(%d);\n", field);
fprintf(out, " opt->primaryFields.push_back(%d);\n", field);
}
fprintf(out, " opt.exclusiveField = %d;\n", atom->exclusiveField);
fprintf(out, " options[static_cast<int>(%s)] = opt;\n",
make_constant_name(atom->name).c_str());
fprintf(out, " opt->exclusiveField = %d;\n", atom->exclusiveField);
if (atom->defaultState != INT_MAX) {
fprintf(out, " opt->defaultState = %d;\n", atom->defaultState);
} else {
fprintf(out, " opt->defaultState = UNSET_VALUE;\n");
}
if (atom->resetState != INT_MAX) {
fprintf(out, " opt->resetState = %d;\n", atom->resetState);
} else {
fprintf(out, " opt->resetState = UNSET_VALUE;\n");
}
fprintf(out, " opt->nested = %d;\n", atom->nested);
}
fprintf(out, " return options;\n");

View File

@@ -148,53 +148,42 @@ message GoodStateAtoms {
// The atom has only primary field but no exclusive state field.
message BadStateAtom1 {
optional int32 uid = 1
[(android.os.statsd.state_field_option).option = PRIMARY];
optional int32 uid = 1 [(android.os.statsd.state_field_option).option = PRIMARY_FIELD];
}
// Only primative types can be annotated.
message BadStateAtom2 {
repeated android.os.statsd.AttributionNode attribution = 1
[(android.os.statsd.state_field_option).option = PRIMARY];
optional int32 state = 2
[(android.os.statsd.state_field_option).option = EXCLUSIVE];
[(android.os.statsd.state_field_option).option = PRIMARY_FIELD];
optional int32 state = 2 [(android.os.statsd.state_field_option).option = EXCLUSIVE_STATE];
}
// Having 2 exclusive state field in the atom means the atom is badly designed.
// E.g., putting bluetooth state and wifi state in the same atom.
message BadStateAtom3 {
optional int32 uid = 1
[(android.os.statsd.state_field_option).option = PRIMARY];
optional int32 state = 2
[(android.os.statsd.state_field_option).option = EXCLUSIVE];
optional int32 state2 = 3
[(android.os.statsd.state_field_option).option = EXCLUSIVE];
optional int32 uid = 1 [(android.os.statsd.state_field_option).option = PRIMARY_FIELD];
optional int32 state = 2 [(android.os.statsd.state_field_option).option = EXCLUSIVE_STATE];
optional int32 state2 = 3 [(android.os.statsd.state_field_option).option = EXCLUSIVE_STATE];
}
message GoodStateAtom1 {
optional int32 uid = 1
[(android.os.statsd.state_field_option).option = PRIMARY];
optional int32 state = 2
[(android.os.statsd.state_field_option).option = EXCLUSIVE];
optional int32 uid = 1 [(android.os.statsd.state_field_option).option = PRIMARY_FIELD];
optional int32 state = 2 [(android.os.statsd.state_field_option).option = EXCLUSIVE_STATE];
}
// Atoms can have exclusive state field, but no primary field. That means
// the state is globally exclusive (e.g., DisplayState).
message GoodStateAtom2 {
optional int32 uid = 1;
optional int32 state = 2
[(android.os.statsd.state_field_option).option = EXCLUSIVE];
optional int32 state = 2 [(android.os.statsd.state_field_option).option = EXCLUSIVE_STATE];
}
// We can have more than one primary fields. That means their combination is a
// primary key.
message GoodStateAtom3 {
optional int32 uid = 1
[(android.os.statsd.state_field_option).option = PRIMARY];
optional int32 tid = 2
[(android.os.statsd.state_field_option).option = PRIMARY];
optional int32 state = 3
[(android.os.statsd.state_field_option).option = EXCLUSIVE];
optional int32 uid = 1 [(android.os.statsd.state_field_option).option = PRIMARY_FIELD];
optional int32 tid = 2 [(android.os.statsd.state_field_option).option = PRIMARY_FIELD];
optional int32 state = 3 [(android.os.statsd.state_field_option).option = EXCLUSIVE_STATE];
}
message WhitelistedAtom {