Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f7e56a64 authored by Christine Tsai's avatar Christine Tsai Committed by Android (Google) Code Review
Browse files

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

parents f47fbd81 5adc7e00
Loading
Loading
Loading
Loading
+49 −20
Original line number Diff line number Diff line
@@ -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.
+34 −18
Original line number Diff line number Diff line
@@ -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;
+62 −18
Original line number Diff line number Diff line
@@ -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,7 +174,9 @@ void StateTracker::updateState(const HashableDimensionKey& primaryKey, const int
        *oldState = mDefaultState;
    }

    // update state map
    // 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")
@@ -174,8 +186,40 @@ void StateTracker::updateState(const HashableDimensionKey& primaryKey, const int
            mStateMap[primaryKey].count = 1;
        }
        *newState = eventState;
        return;
    }

    // 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
+2 −0
Original line number Diff line number Diff line
@@ -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;

+98 −5
Original line number Diff line number Diff line
@@ -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));
}

/**
Loading