- Added #onStateChanged logic to ValueMetricProducer - #onDumpReport fills in slice_by_state values in ValueMetricProducer - Base information is tracked in a separate data structure and keyed by dimensions_in_what HashableDimensionKey. The current state key is also stored in base info so we can close the interval for this state once we get a new state key. - Added unit tests for ValueMetricProducer onStateChange Other changes: - mCondition parameter was removed from #pullAndMatchEventsLocked and #accumulateEvents because it is unused in these functions. - The event key is initialized in each metric's internal onMatchedLogEvent function. This allows ValueMetric to skip events that have primary keys that do not match the current state change primary key. Test: bit statsd_test:* Change-Id: I565c6d251262be57abf10fdef243b8bdc01f3772
390 lines
15 KiB
C++
390 lines
15 KiB
C++
/*
|
|
* Copyright (C) 2019, The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#include <gtest/gtest.h>
|
|
#include "state/StateManager.h"
|
|
#include "state/StateTracker.h"
|
|
#include "state/StateListener.h"
|
|
|
|
#include "tests/statsd_test_util.h"
|
|
|
|
#ifdef __ANDROID__
|
|
|
|
namespace android {
|
|
namespace os {
|
|
namespace statsd {
|
|
|
|
/**
|
|
* Mock StateListener class for testing.
|
|
* Stores primary key and state pairs.
|
|
*/
|
|
class TestStateListener : public virtual StateListener {
|
|
public:
|
|
TestStateListener(){};
|
|
|
|
virtual ~TestStateListener(){};
|
|
|
|
struct Update {
|
|
Update(const HashableDimensionKey& key, int state) : mKey(key), mState(state){};
|
|
HashableDimensionKey mKey;
|
|
int mState;
|
|
};
|
|
|
|
std::vector<Update> updates;
|
|
|
|
void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
|
|
const HashableDimensionKey& primaryKey, int oldState, int newState) {
|
|
updates.emplace_back(primaryKey, newState);
|
|
}
|
|
};
|
|
|
|
int getStateInt(StateManager& mgr, int atomId, const HashableDimensionKey& queryKey) {
|
|
FieldValue output;
|
|
mgr.getStateValue(atomId, queryKey, &output);
|
|
return output.mValue.int_value;
|
|
}
|
|
|
|
// START: build event functions.
|
|
// State with no primary fields - ScreenStateChanged
|
|
std::shared_ptr<LogEvent> buildScreenEvent(int state) {
|
|
std::shared_ptr<LogEvent> event =
|
|
std::make_shared<LogEvent>(android::util::SCREEN_STATE_CHANGED, 1000 /*timestamp*/);
|
|
event->write((int32_t)state);
|
|
event->init();
|
|
return event;
|
|
}
|
|
|
|
// State with one primary field - UidProcessStateChanged
|
|
std::shared_ptr<LogEvent> buildUidProcessEvent(int uid, int state) {
|
|
std::shared_ptr<LogEvent> event =
|
|
std::make_shared<LogEvent>(android::util::UID_PROCESS_STATE_CHANGED, 1000 /*timestamp*/);
|
|
event->write((int32_t)uid);
|
|
event->write((int32_t)state);
|
|
event->init();
|
|
return event;
|
|
}
|
|
|
|
// State with multiple primary fields - OverlayStateChanged
|
|
std::shared_ptr<LogEvent> buildOverlayEvent(int uid, const std::string& packageName, int state) {
|
|
std::shared_ptr<LogEvent> event =
|
|
std::make_shared<LogEvent>(android::util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/);
|
|
event->write((int32_t)uid);
|
|
event->write(packageName);
|
|
event->write(true); // using_alert_window
|
|
event->write((int32_t)state);
|
|
event->init();
|
|
return event;
|
|
}
|
|
|
|
// Incorrect event - missing fields
|
|
std::shared_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName, int state) {
|
|
std::shared_ptr<LogEvent> event =
|
|
std::make_shared<LogEvent>(android::util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/);
|
|
event->write((int32_t)uid);
|
|
event->write(packageName);
|
|
event->write((int32_t)state);
|
|
event->init();
|
|
return event;
|
|
}
|
|
|
|
// Incorrect event - exclusive state has wrong type
|
|
std::shared_ptr<LogEvent> buildOverlayEventBadStateType(int uid, const std::string& packageName) {
|
|
std::shared_ptr<LogEvent> event =
|
|
std::make_shared<LogEvent>(android::util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/);
|
|
event->write((int32_t)uid);
|
|
event->write(packageName);
|
|
event->write(true);
|
|
event->write("string"); // exclusive state: string instead of int
|
|
event->init();
|
|
return event;
|
|
}
|
|
// END: build event functions.
|
|
|
|
// START: get primary key functions
|
|
void getUidProcessKey(int uid, HashableDimensionKey* key) {
|
|
int pos1[] = {1, 0, 0};
|
|
Field field1(27 /* atom id */, pos1, 0 /* depth */);
|
|
Value value1((int32_t)uid);
|
|
|
|
key->addValue(FieldValue(field1, value1));
|
|
}
|
|
|
|
void getOverlayKey(int uid, string packageName, HashableDimensionKey* key) {
|
|
int pos1[] = {1, 0, 0};
|
|
int pos2[] = {2, 0, 0};
|
|
|
|
Field field1(59 /* atom id */, pos1, 0 /* depth */);
|
|
Field field2(59 /* atom id */, pos2, 0 /* depth */);
|
|
|
|
Value value1((int32_t)uid);
|
|
Value value2(packageName);
|
|
|
|
key->addValue(FieldValue(field1, value1));
|
|
key->addValue(FieldValue(field2, value2));
|
|
}
|
|
// END: get primary key functions
|
|
|
|
TEST(StateListenerTest, TestStateListenerWeakPointer) {
|
|
sp<TestStateListener> listener = new TestStateListener();
|
|
wp<TestStateListener> wListener = listener;
|
|
listener = nullptr; // let go of listener
|
|
EXPECT_TRUE(wListener.promote() == nullptr);
|
|
}
|
|
|
|
TEST(StateManagerTest, TestStateManagerGetInstance) {
|
|
sp<TestStateListener> listener1 = new TestStateListener();
|
|
StateManager& mgr = StateManager::getInstance();
|
|
mgr.clear();
|
|
|
|
mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
|
|
EXPECT_EQ(1, mgr.getStateTrackersCount());
|
|
EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
|
|
}
|
|
|
|
/**
|
|
* Test registering listeners to StateTrackers
|
|
*
|
|
* - StateManager will create a new StateTracker if it doesn't already exist
|
|
* and then register the listener to the StateTracker.
|
|
* - If a listener is already registered to a StateTracker, it is not added again.
|
|
* - StateTrackers are only created for atoms that are state atoms.
|
|
*/
|
|
TEST(StateTrackerTest, TestRegisterListener) {
|
|
sp<TestStateListener> listener1 = new TestStateListener();
|
|
sp<TestStateListener> listener2 = new TestStateListener();
|
|
StateManager mgr;
|
|
|
|
// Register listener to non-existing StateTracker
|
|
EXPECT_EQ(0, mgr.getStateTrackersCount());
|
|
EXPECT_TRUE(mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1));
|
|
EXPECT_EQ(1, mgr.getStateTrackersCount());
|
|
EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
|
|
|
|
// Register listener to existing StateTracker
|
|
EXPECT_TRUE(mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2));
|
|
EXPECT_EQ(1, mgr.getStateTrackersCount());
|
|
EXPECT_EQ(2, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
|
|
|
|
// Register already registered listener to existing StateTracker
|
|
EXPECT_TRUE(mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2));
|
|
EXPECT_EQ(1, mgr.getStateTrackersCount());
|
|
EXPECT_EQ(2, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
|
|
|
|
// Register listener to non-state atom
|
|
EXPECT_FALSE(mgr.registerListener(android::util::BATTERY_LEVEL_CHANGED, listener2));
|
|
EXPECT_EQ(1, mgr.getStateTrackersCount());
|
|
}
|
|
|
|
/**
|
|
* Test unregistering listeners from StateTrackers
|
|
*
|
|
* - StateManager will unregister listeners from a StateTracker only if the
|
|
* StateTracker exists and the listener is registered to the StateTracker.
|
|
* - Once all listeners are removed from a StateTracker, the StateTracker
|
|
* is also removed.
|
|
*/
|
|
TEST(StateTrackerTest, TestUnregisterListener) {
|
|
sp<TestStateListener> listener1 = new TestStateListener();
|
|
sp<TestStateListener> listener2 = new TestStateListener();
|
|
StateManager mgr;
|
|
|
|
// Unregister listener from non-existing StateTracker
|
|
EXPECT_EQ(0, mgr.getStateTrackersCount());
|
|
mgr.unregisterListener(android::util::SCREEN_STATE_CHANGED, listener1);
|
|
EXPECT_EQ(0, mgr.getStateTrackersCount());
|
|
EXPECT_EQ(-1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
|
|
|
|
// Unregister non-registered listener from existing StateTracker
|
|
mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
|
|
EXPECT_EQ(1, mgr.getStateTrackersCount());
|
|
EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
|
|
mgr.unregisterListener(android::util::SCREEN_STATE_CHANGED, listener2);
|
|
EXPECT_EQ(1, mgr.getStateTrackersCount());
|
|
EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
|
|
|
|
// Unregister second-to-last listener from StateTracker
|
|
mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2);
|
|
mgr.unregisterListener(android::util::SCREEN_STATE_CHANGED, listener1);
|
|
EXPECT_EQ(1, mgr.getStateTrackersCount());
|
|
EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
|
|
|
|
// Unregister last listener from StateTracker
|
|
mgr.unregisterListener(android::util::SCREEN_STATE_CHANGED, listener2);
|
|
EXPECT_EQ(0, mgr.getStateTrackersCount());
|
|
EXPECT_EQ(-1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
|
|
}
|
|
|
|
/**
|
|
* Test StateManager's onLogEvent and StateListener's onStateChanged correctly
|
|
* updates listener for states without primary keys.
|
|
*/
|
|
TEST(StateTrackerTest, TestStateChangeNoPrimaryFields) {
|
|
sp<TestStateListener> listener1 = new TestStateListener();
|
|
StateManager mgr;
|
|
mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
|
|
|
|
// log event
|
|
std::shared_ptr<LogEvent> event =
|
|
buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON);
|
|
mgr.onLogEvent(*event);
|
|
|
|
// check listener was updated
|
|
EXPECT_EQ(1, listener1->updates.size());
|
|
EXPECT_EQ(DEFAULT_DIMENSION_KEY, listener1->updates[0].mKey);
|
|
EXPECT_EQ(2, listener1->updates[0].mState);
|
|
|
|
// check StateTracker was updated by querying for state
|
|
HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY;
|
|
EXPECT_EQ(2, getStateInt(mgr, android::util::SCREEN_STATE_CHANGED, queryKey));
|
|
}
|
|
|
|
/**
|
|
* Test StateManager's onLogEvent and StateListener's onStateChanged correctly
|
|
* updates listener for states with one primary key.
|
|
*/
|
|
TEST(StateTrackerTest, TestStateChangeOnePrimaryField) {
|
|
sp<TestStateListener> listener1 = new TestStateListener();
|
|
StateManager mgr;
|
|
mgr.registerListener(android::util::UID_PROCESS_STATE_CHANGED, listener1);
|
|
|
|
// log event
|
|
std::shared_ptr<LogEvent> event =
|
|
buildUidProcessEvent(1000 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP);
|
|
mgr.onLogEvent(*event);
|
|
|
|
// check listener was updated
|
|
EXPECT_EQ(1, listener1->updates.size());
|
|
EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
|
|
EXPECT_EQ(1002, listener1->updates[0].mState);
|
|
|
|
// check StateTracker was updated by querying for state
|
|
HashableDimensionKey queryKey;
|
|
getUidProcessKey(1000 /* uid */, &queryKey);
|
|
EXPECT_EQ(1002, getStateInt(mgr, android::util::UID_PROCESS_STATE_CHANGED, queryKey));
|
|
}
|
|
|
|
/**
|
|
* Test StateManager's onLogEvent and StateListener's onStateChanged correctly
|
|
* updates listener for states with multiple primary keys.
|
|
*/
|
|
TEST(StateTrackerTest, TestStateChangeMultiplePrimaryFields) {
|
|
sp<TestStateListener> listener1 = new TestStateListener();
|
|
StateManager mgr;
|
|
mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener1);
|
|
|
|
// log event
|
|
std::shared_ptr<LogEvent> event =
|
|
buildOverlayEvent(1000 /* uid */, "package1", 1); // state: ENTERED
|
|
mgr.onLogEvent(*event);
|
|
|
|
// check listener was updated
|
|
EXPECT_EQ(1, listener1->updates.size());
|
|
EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
|
|
EXPECT_EQ(1, listener1->updates[0].mState);
|
|
|
|
// check StateTracker was updated by querying for state
|
|
HashableDimensionKey queryKey;
|
|
getOverlayKey(1000 /* uid */, "package1", &queryKey);
|
|
EXPECT_EQ(1, getStateInt(mgr, android::util::OVERLAY_STATE_CHANGED, queryKey));
|
|
}
|
|
|
|
/**
|
|
* Test StateManager's onLogEvent and StateListener's onStateChanged
|
|
* when there is an error extracting state from log event. Listener is not
|
|
* updated of state change.
|
|
*/
|
|
TEST(StateTrackerTest, TestStateChangeEventError) {
|
|
sp<TestStateListener> listener1 = new TestStateListener();
|
|
StateManager mgr;
|
|
mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener1);
|
|
|
|
// log event
|
|
std::shared_ptr<LogEvent> event1 =
|
|
buildIncorrectOverlayEvent(1000 /* uid */, "package1", 1 /* state */);
|
|
std::shared_ptr<LogEvent> event2 = buildOverlayEventBadStateType(1001 /* uid */, "package2");
|
|
|
|
// check listener was updated
|
|
mgr.onLogEvent(*event1);
|
|
EXPECT_EQ(0, listener1->updates.size());
|
|
mgr.onLogEvent(*event2);
|
|
EXPECT_EQ(0, listener1->updates.size());
|
|
}
|
|
|
|
TEST(StateTrackerTest, TestStateQuery) {
|
|
sp<TestStateListener> listener1 = new TestStateListener();
|
|
sp<TestStateListener> listener2 = new TestStateListener();
|
|
sp<TestStateListener> listener3 = new TestStateListener();
|
|
StateManager mgr;
|
|
mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
|
|
mgr.registerListener(android::util::UID_PROCESS_STATE_CHANGED, listener2);
|
|
mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener3);
|
|
|
|
std::shared_ptr<LogEvent> event1 = buildUidProcessEvent(
|
|
1000,
|
|
android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002
|
|
std::shared_ptr<LogEvent> event2 = buildUidProcessEvent(
|
|
1001,
|
|
android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE); // state value:
|
|
// 1003
|
|
std::shared_ptr<LogEvent> event3 = buildUidProcessEvent(
|
|
1002,
|
|
android::app::ProcessStateEnum::PROCESS_STATE_PERSISTENT); // state value: 1000
|
|
std::shared_ptr<LogEvent> event4 = buildUidProcessEvent(
|
|
1001,
|
|
android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002
|
|
std::shared_ptr<LogEvent> event5 =
|
|
buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON);
|
|
std::shared_ptr<LogEvent> event6 = buildOverlayEvent(1000, "package1", 1);
|
|
std::shared_ptr<LogEvent> event7 = buildOverlayEvent(1000, "package2", 2);
|
|
|
|
mgr.onLogEvent(*event1);
|
|
mgr.onLogEvent(*event2);
|
|
mgr.onLogEvent(*event3);
|
|
mgr.onLogEvent(*event5);
|
|
mgr.onLogEvent(*event5);
|
|
mgr.onLogEvent(*event6);
|
|
mgr.onLogEvent(*event7);
|
|
|
|
// Query for UidProcessState of uid 1001
|
|
HashableDimensionKey queryKey1;
|
|
getUidProcessKey(1001, &queryKey1);
|
|
EXPECT_EQ(1003, getStateInt(mgr, android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
|
|
|
|
// Query for UidProcessState of uid 1004 - not in state map
|
|
HashableDimensionKey queryKey2;
|
|
getUidProcessKey(1004, &queryKey2);
|
|
EXPECT_EQ(-1, getStateInt(mgr, android::util::UID_PROCESS_STATE_CHANGED,
|
|
queryKey2)); // default state
|
|
|
|
// Query for UidProcessState of uid 1001 - after change in state
|
|
mgr.onLogEvent(*event4);
|
|
EXPECT_EQ(1002, getStateInt(mgr, android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
|
|
|
|
// Query for ScreenState
|
|
EXPECT_EQ(2, getStateInt(mgr, android::util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY));
|
|
|
|
// Query for OverlayState of uid 1000, package name "package2"
|
|
HashableDimensionKey queryKey3;
|
|
getOverlayKey(1000, "package2", &queryKey3);
|
|
EXPECT_EQ(2, getStateInt(mgr, android::util::OVERLAY_STATE_CHANGED, queryKey3));
|
|
}
|
|
|
|
} // namespace statsd
|
|
} // namespace os
|
|
} // namespace android
|
|
#else
|
|
GTEST_LOG_(INFO) << "This test does nothing.\n";
|
|
#endif
|