Merge "Allow default state, reset state, and nesting for binary states"
This commit is contained in:
committed by
Android (Google) Code Review
commit
f7e56a64e2
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user