[automerger skipped] Merge "Bump apex versions" am: 92a90f4ad0 am: 00cc917767 -s ours am: 81d4b5f050 -s ours
am skip reason: Change-Id I4757f6fb00e506ff3236e7fa234ae8acff6034df with SHA-1 3a79e49cbc is in history
Original change: https://android-review.googlesource.com/c/platform/packages/modules/StatsD/+/1524498
MUST ONLY BE SUBMITTED BY AUTOMERGER
Change-Id: Ia55b0426b83493e5a19d1143531800d6560283aa
diff --git a/aidl/Android.bp b/aidl/Android.bp
index 04339e6..f66cf7c 100644
--- a/aidl/Android.bp
+++ b/aidl/Android.bp
@@ -30,6 +30,7 @@
"android/os/StatsDimensionsValueParcel.aidl",
"android/util/StatsEventParcel.aidl",
],
+ host_supported: true,
backend: {
java: {
enabled: false, // framework-statsd and service-statsd use framework-statsd-aidl-sources
diff --git a/apex/TEST_MAPPING b/apex/TEST_MAPPING
index 93f1087..331fe77 100644
--- a/apex/TEST_MAPPING
+++ b/apex/TEST_MAPPING
@@ -6,5 +6,14 @@
{
"name" : "LibStatsPullTests"
}
+ ],
+
+ "postsubmit" : [
+ {
+ "name" : "CtsStatsdHostTestCases"
+ },
+ {
+ "name" : "GtsStatsdHostTestCases"
+ }
]
}
diff --git a/bin/Android.bp b/bin/Android.bp
index 529eb4f..5fb1c1a 100644
--- a/bin/Android.bp
+++ b/bin/Android.bp
@@ -40,15 +40,16 @@
"src/external/StatsPullerManager.cpp",
"src/external/TrainInfoPuller.cpp",
"src/FieldValue.cpp",
+ "src/flags/flags.cpp",
"src/guardrail/StatsdStats.cpp",
"src/hash.cpp",
"src/HashableDimensionKey.cpp",
"src/logd/LogEvent.cpp",
"src/logd/LogEventQueue.cpp",
- "src/matchers/CombinationLogMatchingTracker.cpp",
+ "src/matchers/CombinationAtomMatchingTracker.cpp",
"src/matchers/EventMatcherWizard.cpp",
"src/matchers/matcher_util.cpp",
- "src/matchers/SimpleLogMatchingTracker.cpp",
+ "src/matchers/SimpleAtomMatchingTracker.cpp",
"src/metadata_util.cpp",
"src/metrics/CountMetricProducer.cpp",
"src/metrics/duration_helper/MaxDurationTracker.cpp",
@@ -57,8 +58,9 @@
"src/metrics/EventMetricProducer.cpp",
"src/metrics/GaugeMetricProducer.cpp",
"src/metrics/MetricProducer.cpp",
- "src/metrics/metrics_manager_util.cpp",
"src/metrics/MetricsManager.cpp",
+ "src/metrics/parsing_utils/config_update_utils.cpp",
+ "src/metrics/parsing_utils/metrics_manager_util.cpp",
"src/metrics/ValueMetricProducer.cpp",
"src/packages/UidMap.cpp",
"src/shell/shell_config.proto",
@@ -91,6 +93,7 @@
"libstatslog_statsd",
"libsysutils",
"libutils",
+ "server_configurable_flags",
"statsd-aidl-ndk_platform",
],
shared_libs: [
@@ -143,7 +146,8 @@
export_generated_headers: ["statslog_statsdtest.h"],
shared_libs: [
"libstatssocket",
- ]
+ "libstatspull",
+ ],
}
cc_library_static {
@@ -157,7 +161,11 @@
],
shared_libs: [
"libstatssocket",
- ]
+ "libstatspull",
+ ],
+ export_shared_lib_headers: [
+ "libstatspull",
+ ],
}
// =========
@@ -188,10 +196,6 @@
// address: true,
//},
},
- debuggable: {
- // Add a flag to enable stats log printing from statsd on debug builds.
- cflags: ["-DVERY_VERBOSE_PRINTING"],
- },
},
proto: {
@@ -263,6 +267,8 @@
"tests/e2e/Anomaly_duration_sum_e2e_test.cpp",
"tests/e2e/Attribution_e2e_test.cpp",
"tests/e2e/ConfigTtl_e2e_test.cpp",
+ "tests/e2e/ConfigUpdate_e2e_ab_test.cpp",
+ "tests/e2e/ConfigUpdate_e2e_test.cpp",
"tests/e2e/CountMetric_e2e_test.cpp",
"tests/e2e/DurationMetric_e2e_test.cpp",
"tests/e2e/GaugeMetric_e2e_pull_test.cpp",
@@ -292,6 +298,8 @@
"tests/metrics/metrics_test_helper.cpp",
"tests/metrics/OringDurationTracker_test.cpp",
"tests/metrics/ValueMetricProducer_test.cpp",
+ "tests/metrics/parsing_utils/config_update_utils_test.cpp",
+ "tests/metrics/parsing_utils/metrics_manager_util_test.cpp",
"tests/MetricsManager_test.cpp",
"tests/shell/ShellSubscriber_test.cpp",
"tests/state/StateTracker_test.cpp",
@@ -405,6 +413,33 @@
},
}
+java_library {
+ name: "statsdprotonano",
+ sdk_version: "9",
+ proto: {
+ type: "nano",
+ output_params: ["store_unknown_fields=true"],
+ include_dirs: [
+ "external/protobuf/src",
+ "frameworks/proto_logging/stats",
+ ],
+ },
+ srcs: [
+ ":libstats_atoms_proto",
+ "src/shell/shell_config.proto",
+ "src/shell/shell_data.proto",
+ "src/stats_log.proto",
+ "src/statsd_config.proto",
+ ],
+ static_libs: [
+ "platformprotosnano",
+ ],
+ // Protos have lots of MissingOverride and similar.
+ errorprone: {
+ javacflags: ["-XepDisableAllChecks"],
+ },
+}
+
// Filegroup for statsd config proto definition.
filegroup {
name: "statsd-config-proto-def",
diff --git a/bin/TEST_MAPPING b/bin/TEST_MAPPING
index 8dee073..a7a4cf1 100644
--- a/bin/TEST_MAPPING
+++ b/bin/TEST_MAPPING
@@ -3,5 +3,15 @@
{
"name" : "statsd_test"
}
+ ],
+
+ "postsubmit" : [
+ {
+ "name" : "CtsStatsdHostTestCases"
+ },
+ {
+ "name" : "GtsStatsdHostTestCases"
+ }
]
-}
\ No newline at end of file
+
+}
diff --git a/bin/src/StatsLogProcessor.cpp b/bin/src/StatsLogProcessor.cpp
index 6de77b9..fa69c45 100644
--- a/bin/src/StatsLogProcessor.cpp
+++ b/bin/src/StatsLogProcessor.cpp
@@ -24,12 +24,13 @@
#include <packages/modules/StatsD/bin/src/active_config_list.pb.h>
#include <packages/modules/StatsD/bin/src/experiment_ids.pb.h>
+#include "StatsService.h"
#include "android-base/stringprintf.h"
#include "external/StatsPullerManager.h"
+#include "flags/flags.h"
#include "guardrail/StatsdStats.h"
#include "logd/LogEvent.h"
#include "metrics/CountMetricProducer.h"
-#include "StatsService.h"
#include "state/StateManager.h"
#include "stats_log_util.h"
#include "stats_util.h"
@@ -408,11 +409,9 @@
onWatchdogRollbackOccurredLocked(event);
}
-#ifdef VERY_VERBOSE_PRINTING
if (mPrintAllLogs) {
ALOGI("%s", event->ToString().c_str());
}
-#endif
resetIfConfigTtlExpiredLocked(eventElapsedTimeNs);
// Hard-coded logic to update the isolated uid's in the uid-map.
@@ -525,22 +524,37 @@
const StatsdConfig& config) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
WriteDataToDiskLocked(key, timestampNs, CONFIG_UPDATED, NO_TIME_CONSTRAINTS);
- OnConfigUpdatedLocked(timestampNs, key, config);
+ bool modularUpdate = getFlagBool(PARTIAL_CONFIG_UPDATE_FLAG, "false");
+ OnConfigUpdatedLocked(timestampNs, key, config, modularUpdate);
}
-void StatsLogProcessor::OnConfigUpdatedLocked(
- const int64_t timestampNs, const ConfigKey& key, const StatsdConfig& config) {
+void StatsLogProcessor::OnConfigUpdatedLocked(const int64_t timestampNs, const ConfigKey& key,
+ const StatsdConfig& config, bool modularUpdate) {
VLOG("Updated configuration for key %s", key.ToString().c_str());
- sp<MetricsManager> newMetricsManager =
- new MetricsManager(key, config, mTimeBaseNs, timestampNs, mUidMap, mPullerManager,
- mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
- if (newMetricsManager->isConfigValid()) {
- newMetricsManager->init();
- mUidMap->OnConfigUpdated(key);
- newMetricsManager->refreshTtl(timestampNs);
- mMetricsManagers[key] = newMetricsManager;
- VLOG("StatsdConfig valid");
+ // Create new config if this is not a modular update or if this is a new config.
+ const auto& it = mMetricsManagers.find(key);
+ bool configValid = false;
+ if (!modularUpdate || it == mMetricsManagers.end()) {
+ sp<MetricsManager> newMetricsManager =
+ new MetricsManager(key, config, mTimeBaseNs, timestampNs, mUidMap, mPullerManager,
+ mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
+ configValid = newMetricsManager->isConfigValid();
+ if (configValid) {
+ newMetricsManager->init();
+ mUidMap->OnConfigUpdated(key);
+ newMetricsManager->refreshTtl(timestampNs);
+ mMetricsManagers[key] = newMetricsManager;
+ VLOG("StatsdConfig valid");
+ }
} else {
+ // Preserve the existing MetricsManager, update necessary components and metadata in place.
+ configValid = it->second->updateConfig(config, mTimeBaseNs, timestampNs,
+ mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
+ if (configValid) {
+ mUidMap->OnConfigUpdated(key);
+ }
+ }
+ if (!configValid) {
// If there is any error in the config, don't use it.
// Remove any existing config with the same key.
ALOGE("StatsdConfig NOT valid");
@@ -706,7 +720,8 @@
for (const auto& key : configs) {
StatsdConfig config;
if (StorageManager::readConfigFromDisk(key, &config)) {
- OnConfigUpdatedLocked(timestampNs, key, config);
+ // Force a full update when resetting a config.
+ OnConfigUpdatedLocked(timestampNs, key, config, /*modularUpdate=*/false);
StatsdStats::getInstance().noteConfigReset(key);
} else {
ALOGE("Failed to read backup config from disk for : %s", key.ToString().c_str());
diff --git a/bin/src/StatsLogProcessor.h b/bin/src/StatsLogProcessor.h
index 099ad4b..4404c18 100644
--- a/bin/src/StatsLogProcessor.h
+++ b/bin/src/StatsLogProcessor.h
@@ -134,10 +134,8 @@
int64_t getLastReportTimeNs(const ConfigKey& key);
inline void setPrintLogs(bool enabled) {
-#ifdef VERY_VERBOSE_PRINTING
std::lock_guard<std::mutex> lock(mMetricsMutex);
mPrintAllLogs = enabled;
-#endif
}
// Add a specific config key to the possible configs to dump ASAP.
@@ -189,8 +187,8 @@
void resetIfConfigTtlExpiredLocked(const int64_t timestampNs);
- void OnConfigUpdatedLocked(
- const int64_t currentTimestampNs, const ConfigKey& key, const StatsdConfig& config);
+ void OnConfigUpdatedLocked(const int64_t currentTimestampNs, const ConfigKey& key,
+ const StatsdConfig& config, bool modularUpdate);
void GetActiveConfigsLocked(const int uid, vector<int64_t>& outActiveConfigs);
@@ -292,9 +290,7 @@
// The time for the next anomaly alarm for alerts.
int64_t mNextAnomalyAlarmTime = 0;
-#ifdef VERY_VERBOSE_PRINTING
bool mPrintAllLogs = false;
-#endif
FRIEND_TEST(StatsLogProcessorTest, TestOutOfOrderLogs);
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize);
@@ -342,6 +338,10 @@
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
+ FRIEND_TEST(ConfigUpdateE2eAbTest, TestHashStrings);
+ FRIEND_TEST(ConfigUpdateE2eAbTest, TestUidMapVersionStringInstaller);
+ FRIEND_TEST(ConfigUpdateE2eAbTest, TestConfigTtl);
+
FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges);
FRIEND_TEST(CountMetricE2eTest, TestSlicedState);
FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap);
diff --git a/bin/src/StatsService.cpp b/bin/src/StatsService.cpp
index ca8326c..94b1736 100644
--- a/bin/src/StatsService.cpp
+++ b/bin/src/StatsService.cpp
@@ -480,7 +480,8 @@
dprintf(out, " Clear cached puller data.\n");
dprintf(out, "\n");
dprintf(out, "usage: adb shell cmd stats print-logs\n");
- dprintf(out, " Only works on eng build\n");
+ dprintf(out, " Requires root privileges.\n");
+ dprintf(out, " Can be disabled by calling adb shell cmd stats print-logs 0\n");
}
status_t StatsService::cmd_trigger_broadcast(int out, Vector<String8>& args) {
@@ -861,18 +862,19 @@
}
status_t StatsService::cmd_print_logs(int out, const Vector<String8>& args) {
- VLOG("StatsService::cmd_print_logs with Pid %i, Uid %i", AIBinder_getCallingPid(),
- AIBinder_getCallingUid());
- if (checkPermission(kPermissionDump)) {
- bool enabled = true;
- if (args.size() >= 2) {
- enabled = atoi(args[1].c_str()) != 0;
- }
- mProcessor->setPrintLogs(enabled);
- return NO_ERROR;
- } else {
+ Status status = checkUid(AID_ROOT);
+ if (!status.isOk()) {
return PERMISSION_DENIED;
}
+
+ VLOG("StatsService::cmd_print_logs with pid %i, uid %i", AIBinder_getCallingPid(),
+ AIBinder_getCallingUid());
+ bool enabled = true;
+ if (args.size() >= 2) {
+ enabled = atoi(args[1].c_str()) != 0;
+ }
+ mProcessor->setPrintLogs(enabled);
+ return NO_ERROR;
}
bool StatsService::getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid) {
diff --git a/bin/src/anomaly/AlarmTracker.h b/bin/src/anomaly/AlarmTracker.h
index aa37192..23defc9 100644
--- a/bin/src/anomaly/AlarmTracker.h
+++ b/bin/src/anomaly/AlarmTracker.h
@@ -73,6 +73,7 @@
FRIEND_TEST(AlarmTrackerTest, TestTriggerTimestamp);
FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms);
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateAlarms);
};
} // namespace statsd
diff --git a/bin/src/anomaly/AnomalyTracker.cpp b/bin/src/anomaly/AnomalyTracker.cpp
index 619752c..6aa410b 100644
--- a/bin/src/anomaly/AnomalyTracker.cpp
+++ b/bin/src/anomaly/AnomalyTracker.cpp
@@ -37,14 +37,6 @@
AnomalyTracker::AnomalyTracker(const Alert& alert, const ConfigKey& configKey)
: mAlert(alert), mConfigKey(configKey), mNumOfPastBuckets(mAlert.num_buckets() - 1) {
VLOG("AnomalyTracker() called");
- if (mAlert.num_buckets() <= 0) {
- ALOGE("Cannot create AnomalyTracker with %lld buckets", (long long)mAlert.num_buckets());
- return;
- }
- if (!mAlert.has_trigger_if_sum_gt()) {
- ALOGE("Cannot create AnomalyTracker without threshold");
- return;
- }
resetStorage(); // initialization
}
@@ -52,6 +44,10 @@
VLOG("~AnomalyTracker() called");
}
+void AnomalyTracker::onConfigUpdated() {
+ mSubscriptions.clear();
+}
+
void AnomalyTracker::resetStorage() {
VLOG("resetStorage() called.");
mPastBuckets.clear();
@@ -259,6 +255,15 @@
return false;
}
+std::pair<bool, uint64_t> AnomalyTracker::getProtoHash() const {
+ string serializedAlert;
+ if (!mAlert.SerializeToString(&serializedAlert)) {
+ ALOGW("Unable to serialize alert %lld", (long long)mAlert.id());
+ return {false, 0};
+ }
+ return {true, Hash64(serializedAlert)};
+}
+
void AnomalyTracker::informSubscribers(const MetricDimensionKey& key, int64_t metric_id,
int64_t metricValue) {
triggerSubscribers(mAlert.id(), metric_id, key, metricValue, mConfigKey, mSubscriptions);
diff --git a/bin/src/anomaly/AnomalyTracker.h b/bin/src/anomaly/AnomalyTracker.h
index e5893a4..7839e06 100644
--- a/bin/src/anomaly/AnomalyTracker.h
+++ b/bin/src/anomaly/AnomalyTracker.h
@@ -16,15 +16,15 @@
#pragma once
-#include <stdlib.h>
-
#include <gtest/gtest_prod.h>
+#include <stdlib.h>
#include <utils/RefBase.h>
#include "AlarmMonitor.h"
#include "config/ConfigKey.h"
-#include "packages/modules/StatsD/bin/src/statsd_config.pb.h" // Alert
+#include "packages/modules/StatsD/bin/src/statsd_config.pb.h" // Alert
#include "packages/modules/StatsD/bin/src/statsd_metadata.pb.h" // AlertMetadata
+#include "hash.h"
#include "stats_util.h" // HashableDimensionKey and DimToValMap
namespace android {
@@ -41,6 +41,9 @@
virtual ~AnomalyTracker();
+ // Reset appropriate state on a config update. Clear subscriptions so they can be reset.
+ void onConfigUpdated();
+
// Add subscriptions that depend on this alert.
void addSubscription(const Subscription& subscription) {
mSubscriptions.push_back(subscription);
@@ -106,6 +109,26 @@
return mNumOfPastBuckets;
}
+ std::pair<bool, uint64_t> getProtoHash() const;
+
+ // Sets an alarm for the given timestamp.
+ // Replaces previous alarm if one already exists.
+ virtual void startAlarm(const MetricDimensionKey& dimensionKey, const int64_t& eventTime) {
+ return; // The base AnomalyTracker class doesn't have alarms.
+ }
+
+ // Stops the alarm.
+ // If it should have already fired, but hasn't yet (e.g. because the AlarmManager is delayed),
+ // declare the anomaly now.
+ virtual void stopAlarm(const MetricDimensionKey& dimensionKey, const int64_t& timestampNs) {
+ return; // The base AnomalyTracker class doesn't have alarms.
+ }
+
+ // Stop all the alarms owned by this tracker. Does not declare any anomalies.
+ virtual void cancelAllAlarms() {
+ return; // The base AnomalyTracker class doesn't have alarms.
+ }
+
// Declares an anomaly for each alarm in firedAlarms that belongs to this AnomalyTracker,
// and removes it from firedAlarms. Does NOT remove the alarm from the AlarmMonitor.
virtual void informAlarmsFired(const int64_t& timestampNs,
@@ -197,6 +220,8 @@
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period);
+
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateAlerts);
};
} // namespace statsd
diff --git a/bin/src/anomaly/DurationAnomalyTracker.h b/bin/src/anomaly/DurationAnomalyTracker.h
index 686d8f9..4641914 100644
--- a/bin/src/anomaly/DurationAnomalyTracker.h
+++ b/bin/src/anomaly/DurationAnomalyTracker.h
@@ -34,15 +34,15 @@
// Sets an alarm for the given timestamp.
// Replaces previous alarm if one already exists.
- void startAlarm(const MetricDimensionKey& dimensionKey, const int64_t& eventTime);
+ void startAlarm(const MetricDimensionKey& dimensionKey, const int64_t& eventTime) override;
// Stops the alarm.
// If it should have already fired, but hasn't yet (e.g. because the AlarmManager is delayed),
// declare the anomaly now.
- void stopAlarm(const MetricDimensionKey& dimensionKey, const int64_t& timestampNs);
+ void stopAlarm(const MetricDimensionKey& dimensionKey, const int64_t& timestampNs) override;
// Stop all the alarms owned by this tracker. Does not declare any anomalies.
- void cancelAllAlarms();
+ void cancelAllAlarms() override;
// Declares an anomaly for each alarm in firedAlarms that belongs to this DurationAnomalyTracker
// and removes it from firedAlarms. The AlarmMonitor is not informed.
diff --git a/bin/src/condition/CombinationConditionTracker.cpp b/bin/src/condition/CombinationConditionTracker.cpp
index e9875ba..4574b2e 100644
--- a/bin/src/condition/CombinationConditionTracker.cpp
+++ b/bin/src/condition/CombinationConditionTracker.cpp
@@ -25,8 +25,9 @@
using std::unordered_map;
using std::vector;
-CombinationConditionTracker::CombinationConditionTracker(const int64_t& id, const int index)
- : ConditionTracker(id, index) {
+CombinationConditionTracker::CombinationConditionTracker(const int64_t& id, const int index,
+ const uint64_t protoHash)
+ : ConditionTracker(id, index, protoHash) {
VLOG("creating CombinationConditionTracker %lld", (long long)mConditionId);
}
@@ -38,9 +39,23 @@
const vector<sp<ConditionTracker>>& allConditionTrackers,
const unordered_map<int64_t, int>& conditionIdIndexMap,
vector<bool>& stack,
- vector<ConditionState>& initialConditionCache) {
+ vector<ConditionState>& conditionCache) {
VLOG("Combination predicate init() %lld", (long long)mConditionId);
if (mInitialized) {
+ // All the children are guaranteed to be initialized, but the recursion is needed to
+ // fill the conditionCache properly, since another combination condition or metric
+ // might rely on this. The recursion is needed to compute the current condition.
+
+ // Init is called instead of isConditionMet so that the ConditionKey can be filled with the
+ // default key for sliced conditions, since we do not know all indirect descendants here.
+ for (const int childIndex : mChildren) {
+ if (conditionCache[childIndex] == ConditionState::kNotEvaluated) {
+ allConditionTrackers[childIndex]->init(allConditionConfig, allConditionTrackers,
+ conditionIdIndexMap, stack, conditionCache);
+ }
+ }
+ conditionCache[mIndex] =
+ evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
return true;
}
@@ -74,9 +89,8 @@
return false;
}
- bool initChildSucceeded =
- childTracker->init(allConditionConfig, allConditionTrackers, conditionIdIndexMap,
- stack, initialConditionCache);
+ bool initChildSucceeded = childTracker->init(allConditionConfig, allConditionTrackers,
+ conditionIdIndexMap, stack, conditionCache);
if (!initChildSucceeded) {
ALOGW("Child initialization failed %lld ", (long long)child);
@@ -92,14 +106,14 @@
mUnSlicedChildren.push_back(childIndex);
}
mChildren.push_back(childIndex);
- mTrackerIndex.insert(childTracker->getLogTrackerIndex().begin(),
- childTracker->getLogTrackerIndex().end());
+ mTrackerIndex.insert(childTracker->getAtomMatchingTrackerIndex().begin(),
+ childTracker->getAtomMatchingTrackerIndex().end());
}
- mUnSlicedPartCondition = evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation,
- initialConditionCache);
- initialConditionCache[mIndex] =
- evaluateCombinationCondition(mChildren, mLogicalOperation, initialConditionCache);
+ mUnSlicedPartCondition =
+ evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation, conditionCache);
+ conditionCache[mIndex] =
+ evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
// unmark this node in the recursion stack.
stack[mIndex] = false;
@@ -109,6 +123,49 @@
return true;
}
+bool CombinationConditionTracker::onConfigUpdated(
+ const vector<Predicate>& allConditionProtos, const int index,
+ const vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ const unordered_map<int64_t, int>& conditionTrackerMap) {
+ ConditionTracker::onConfigUpdated(allConditionProtos, index, allConditionTrackers,
+ atomMatchingTrackerMap, conditionTrackerMap);
+ mTrackerIndex.clear();
+ mChildren.clear();
+ mUnSlicedChildren.clear();
+ mSlicedChildren.clear();
+ Predicate_Combination combinationCondition = allConditionProtos[mIndex].combination();
+
+ for (const int64_t child : combinationCondition.predicate()) {
+ const auto& it = conditionTrackerMap.find(child);
+
+ if (it == conditionTrackerMap.end()) {
+ ALOGW("Predicate %lld not found in the config", (long long)child);
+ return false;
+ }
+
+ int childIndex = it->second;
+ const sp<ConditionTracker>& childTracker = allConditionTrackers[childIndex];
+
+ // Ensures that the child's tracker indices are updated.
+ if (!childTracker->onConfigUpdated(allConditionProtos, childIndex, allConditionTrackers,
+ atomMatchingTrackerMap, conditionTrackerMap)) {
+ ALOGW("Child update failed %lld ", (long long)child);
+ return false;
+ }
+
+ if (allConditionTrackers[childIndex]->isSliced()) {
+ mSlicedChildren.push_back(childIndex);
+ } else {
+ mUnSlicedChildren.push_back(childIndex);
+ }
+ mChildren.push_back(childIndex);
+ mTrackerIndex.insert(childTracker->getAtomMatchingTrackerIndex().begin(),
+ childTracker->getAtomMatchingTrackerIndex().end());
+ }
+ return true;
+}
+
void CombinationConditionTracker::isConditionMet(
const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
const bool isPartialLink,
diff --git a/bin/src/condition/CombinationConditionTracker.h b/bin/src/condition/CombinationConditionTracker.h
index 0c598c2..329f707 100644
--- a/bin/src/condition/CombinationConditionTracker.h
+++ b/bin/src/condition/CombinationConditionTracker.h
@@ -24,16 +24,21 @@
namespace os {
namespace statsd {
-class CombinationConditionTracker : public virtual ConditionTracker {
+class CombinationConditionTracker : public ConditionTracker {
public:
- CombinationConditionTracker(const int64_t& id, const int index);
+ CombinationConditionTracker(const int64_t& id, const int index, const uint64_t protoHash);
~CombinationConditionTracker();
bool init(const std::vector<Predicate>& allConditionConfig,
const std::vector<sp<ConditionTracker>>& allConditionTrackers,
const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack,
- std::vector<ConditionState>& initialConditionCache) override;
+ std::vector<ConditionState>& conditionCache) override;
+
+ bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index,
+ const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap) override;
void evaluateCondition(const LogEvent& event,
const std::vector<MatchingState>& eventMatcherValues,
@@ -80,16 +85,14 @@
const std::vector<sp<ConditionTracker>>& allConditions,
const vector<Matcher>& dimensions) const override;
- void getTrueSlicedDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions,
- std::set<HashableDimensionKey>* dimensions) const override {
+ const std::map<HashableDimensionKey, int>* getSlicedDimensionMap(
+ const std::vector<sp<ConditionTracker>>& allConditions) const override {
if (mSlicedChildren.size() == 1) {
- return allConditions[mSlicedChildren.front()]->getTrueSlicedDimensions(
- allConditions, dimensions);
+ return allConditions[mSlicedChildren.front()]->getSlicedDimensionMap(allConditions);
}
+ return nullptr;
}
-
private:
LogicalOperation mLogicalOperation;
@@ -102,6 +105,7 @@
std::vector<int> mSlicedChildren;
std::vector<int> mUnSlicedChildren;
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateConditions);
};
} // namespace statsd
diff --git a/bin/src/condition/ConditionTimer.h b/bin/src/condition/ConditionTimer.h
index 442bc11..1fbe252 100644
--- a/bin/src/condition/ConditionTimer.h
+++ b/bin/src/condition/ConditionTimer.h
@@ -36,7 +36,7 @@
public:
explicit ConditionTimer(bool initCondition, int64_t bucketStartNs) : mCondition(initCondition) {
if (initCondition) {
- mLastConditionTrueTimestampNs = bucketStartNs;
+ mLastConditionChangeTimestampNs = bucketStartNs;
}
};
@@ -44,21 +44,46 @@
// When a new bucket is created, this value will be reset to 0.
int64_t mTimerNs = 0;
- // Last elapsed real timestamp when condition turned to true
- // When a new bucket is created and the condition is true, then the timestamp is set
- // to be the bucket start timestamp.
- int64_t mLastConditionTrueTimestampNs = 0;
+ // Last elapsed real timestamp when condition changed.
+ int64_t mLastConditionChangeTimestampNs = 0;
bool mCondition = false;
int64_t newBucketStart(int64_t nextBucketStartNs) {
if (mCondition) {
- mTimerNs += (nextBucketStartNs - mLastConditionTrueTimestampNs);
- mLastConditionTrueTimestampNs = nextBucketStartNs;
+ // Normally, the next bucket happens after the last condition
+ // change. In this case, add the time between the condition becoming
+ // true to the next bucket start time.
+ // Otherwise, the next bucket start time is before the last
+ // condition change time, this means that the condition was false at
+ // the bucket boundary before the condition became true, so the
+ // timer should not get updated and the last condition change time
+ // remains as is.
+ if (nextBucketStartNs >= mLastConditionChangeTimestampNs) {
+ mTimerNs += (nextBucketStartNs - mLastConditionChangeTimestampNs);
+ mLastConditionChangeTimestampNs = nextBucketStartNs;
+ }
+ } else if (mLastConditionChangeTimestampNs > nextBucketStartNs) {
+ // The next bucket start time is before the last condition change
+ // time, this means that the condition was true at the bucket
+ // boundary before the condition became false, so adjust the timer
+ // to match how long the condition was true to the bucket boundary.
+ // This means remove the amount the condition stayed true in the
+ // next bucket from the current bucket.
+ mTimerNs -= (mLastConditionChangeTimestampNs - nextBucketStartNs);
}
int64_t temp = mTimerNs;
mTimerNs = 0;
+
+ if (!mCondition && (mLastConditionChangeTimestampNs > nextBucketStartNs)) {
+ // The next bucket start time is before the last condition change
+ // time, this means that the condition was true at the bucket
+ // boundary and remained true in the next bucket up to the condition
+ // change to false, so adjust the timer to match how long the
+ // condition stayed true in the next bucket (now the current bucket).
+ mTimerNs = mLastConditionChangeTimestampNs - nextBucketStartNs;
+ }
return temp;
}
@@ -67,11 +92,10 @@
return;
}
mCondition = newCondition;
- if (newCondition) {
- mLastConditionTrueTimestampNs = timestampNs;
- } else {
- mTimerNs += (timestampNs - mLastConditionTrueTimestampNs);
+ if (newCondition == false) {
+ mTimerNs += (timestampNs - mLastConditionChangeTimestampNs);
}
+ mLastConditionChangeTimestampNs = timestampNs;
}
FRIEND_TEST(ConditionTimerTest, TestTimer_Inital_False);
@@ -80,4 +104,4 @@
} // namespace statsd
} // namespace os
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/bin/src/condition/ConditionTracker.h b/bin/src/condition/ConditionTracker.h
index b8448f7..84095f0 100644
--- a/bin/src/condition/ConditionTracker.h
+++ b/bin/src/condition/ConditionTracker.h
@@ -18,7 +18,7 @@
#include "condition/condition_util.h"
#include "packages/modules/StatsD/bin/src/statsd_config.pb.h"
-#include "matchers/LogMatchingTracker.h"
+#include "matchers/AtomMatchingTracker.h"
#include "matchers/matcher_util.h"
#include <utils/RefBase.h>
@@ -31,38 +31,59 @@
class ConditionTracker : public virtual RefBase {
public:
- ConditionTracker(const int64_t& id, const int index)
+ ConditionTracker(const int64_t& id, const int index, const uint64_t protoHash)
: mConditionId(id),
mIndex(index),
mInitialized(false),
mTrackerIndex(),
mUnSlicedPartCondition(ConditionState::kUnknown),
- mSliced(false){};
+ mSliced(false),
+ mProtoHash(protoHash){};
virtual ~ConditionTracker(){};
- inline const int64_t& getId() { return mConditionId; }
-
// Initialize this ConditionTracker. This initialization is done recursively (DFS). It can also
// be done in the constructor, but we do it separately because (1) easy to return a bool to
// indicate whether the initialization is successful. (2) makes unit test easier.
+ // This function can also be called on config updates, in which case it does nothing other than
+ // fill the condition cache with the current condition.
// allConditionConfig: the list of all Predicate config from statsd_config.
// allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also
- // need to call init() on children conditions)
+ // need to call init() on child conditions)
// conditionIdIndexMap: the mapping from condition id to its index.
// stack: a bit map to keep track which nodes have been visited on the stack in the recursion.
- // initialConditionCache: tracks initial conditions of all ConditionTrackers.
+ // conditionCache: tracks initial conditions of all ConditionTrackers. returns the
+ // current condition if called on a config update.
virtual bool init(const std::vector<Predicate>& allConditionConfig,
const std::vector<sp<ConditionTracker>>& allConditionTrackers,
const std::unordered_map<int64_t, int>& conditionIdIndexMap,
- std::vector<bool>& stack,
- std::vector<ConditionState>& initialConditionCache) = 0;
+ std::vector<bool>& stack, std::vector<ConditionState>& conditionCache) = 0;
+
+ // Update appropriate state on config updates. Primarily, all indices need to be updated.
+ // This predicate and all of its children are guaranteed to be preserved across the update.
+ // This function is recursive and will call onConfigUpdated on child conditions. It does not
+ // manage cycle detection since all preserved conditions should not have any cycles.
+ //
+ // allConditionProtos: the new predicates.
+ // index: the new index of this tracker in allConditionProtos and allConditionTrackers.
+ // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also
+ // need to call onConfigUpdated() on child conditions)
+ // atomMatchingTrackerMap: map of atom matcher id to index after the config update.
+ // conditionTrackerMap: map of condition tracker id to index after the config update.
+ // returns whether or not the update is successful.
+ virtual bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index,
+ const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap) {
+ mIndex = index;
+ return true;
+ }
// evaluate current condition given the new event.
// event: the new log event
- // eventMatcherValues: the results of the LogMatcherTrackers. LogMatcherTrackers always process
- // event before ConditionTrackers, because ConditionTracker depends on
- // LogMatchingTrackers.
+ // eventMatcherValues: the results of the AtomMatchingTrackers. AtomMatchingTrackers always
+ // process event before ConditionTrackers, because ConditionTracker depends
+ // on AtomMatchingTrackers.
// mAllConditions: the list of all ConditionTracker
// conditionCache: the cached non-sliced condition of the ConditionTrackers for this new event.
// conditionChanged: the bit map to record whether the condition has changed.
@@ -88,8 +109,8 @@
const bool isPartialLink,
std::vector<ConditionState>& conditionCache) const = 0;
- // return the list of LogMatchingTracker index that this ConditionTracker uses.
- virtual const std::set<int>& getLogTrackerIndex() const {
+ // return the list of AtomMatchingTracker index that this ConditionTracker uses.
+ virtual const std::set<int>& getAtomMatchingTrackerIndex() const {
return mTrackerIndex;
}
@@ -110,9 +131,12 @@
return mConditionId;
}
- virtual void getTrueSlicedDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions,
- std::set<HashableDimensionKey>* dimensions) const = 0;
+ inline uint64_t getProtoHash() const {
+ return mProtoHash;
+ }
+
+ virtual const std::map<HashableDimensionKey, int>* getSlicedDimensionMap(
+ const std::vector<sp<ConditionTracker>>& allConditions) const = 0;
virtual bool IsChangedDimensionTrackable() const = 0;
@@ -131,12 +155,12 @@
const int64_t mConditionId;
// the index of this condition in the manager's condition list.
- const int mIndex;
+ int mIndex;
// if it's properly initialized.
bool mInitialized;
- // the list of LogMatchingTracker index that this ConditionTracker uses.
+ // the list of AtomMatchingTracker index that this ConditionTracker uses.
std::set<int> mTrackerIndex;
// This variable is only used for CombinationConditionTrackers.
@@ -149,6 +173,12 @@
ConditionState mUnSlicedPartCondition;
bool mSliced;
+
+ // Hash of the Predicate's proto bytes from StatsdConfig.
+ // Used to determine if the definition of this condition has changed across a config update.
+ const uint64_t mProtoHash;
+
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateConditions);
};
} // namespace statsd
diff --git a/bin/src/condition/ConditionWizard.h b/bin/src/condition/ConditionWizard.h
index 8926479..43db94c 100644
--- a/bin/src/condition/ConditionWizard.h
+++ b/bin/src/condition/ConditionWizard.h
@@ -53,9 +53,9 @@
ConditionState getUnSlicedPartConditionState(const int index) {
return mAllConditions[index]->getUnSlicedPartConditionState();
}
- void getTrueSlicedDimensions(const int index,
- std::set<HashableDimensionKey>* trueDimensions) const {
- return mAllConditions[index]->getTrueSlicedDimensions(mAllConditions, trueDimensions);
+
+ const std::map<HashableDimensionKey, int>* getSlicedDimensionMap(const int index) const {
+ return mAllConditions[index]->getSlicedDimensionMap(mAllConditions);
}
private:
diff --git a/bin/src/condition/SimpleConditionTracker.cpp b/bin/src/condition/SimpleConditionTracker.cpp
index efb4d49..1dcc8f9 100644
--- a/bin/src/condition/SimpleConditionTracker.cpp
+++ b/bin/src/condition/SimpleConditionTracker.cpp
@@ -27,54 +27,21 @@
using std::unordered_map;
SimpleConditionTracker::SimpleConditionTracker(
- const ConfigKey& key, const int64_t& id, const int index,
+ const ConfigKey& key, const int64_t& id, const uint64_t protoHash, const int index,
const SimplePredicate& simplePredicate,
- const unordered_map<int64_t, int>& trackerNameIndexMap)
- : ConditionTracker(id, index), mConfigKey(key), mContainANYPositionInInternalDimensions(false) {
+ const unordered_map<int64_t, int>& atomMatchingTrackerMap)
+ : ConditionTracker(id, index, protoHash),
+ mConfigKey(key),
+ mContainANYPositionInInternalDimensions(false) {
VLOG("creating SimpleConditionTracker %lld", (long long)mConditionId);
mCountNesting = simplePredicate.count_nesting();
- if (simplePredicate.has_start()) {
- auto pair = trackerNameIndexMap.find(simplePredicate.start());
- if (pair == trackerNameIndexMap.end()) {
- ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start());
- return;
- }
- mStartLogMatcherIndex = pair->second;
- mTrackerIndex.insert(mStartLogMatcherIndex);
- } else {
- mStartLogMatcherIndex = -1;
- }
-
- if (simplePredicate.has_stop()) {
- auto pair = trackerNameIndexMap.find(simplePredicate.stop());
- if (pair == trackerNameIndexMap.end()) {
- ALOGW("Stop matcher %lld not found in the config", (long long)simplePredicate.stop());
- return;
- }
- mStopLogMatcherIndex = pair->second;
- mTrackerIndex.insert(mStopLogMatcherIndex);
- } else {
- mStopLogMatcherIndex = -1;
- }
-
- if (simplePredicate.has_stop_all()) {
- auto pair = trackerNameIndexMap.find(simplePredicate.stop_all());
- if (pair == trackerNameIndexMap.end()) {
- ALOGW("Stop all matcher %lld found in the config", (long long)simplePredicate.stop_all());
- return;
- }
- mStopAllLogMatcherIndex = pair->second;
- mTrackerIndex.insert(mStopAllLogMatcherIndex);
- } else {
- mStopAllLogMatcherIndex = -1;
- }
+ setMatcherIndices(simplePredicate, atomMatchingTrackerMap);
if (simplePredicate.has_dimensions()) {
translateFieldMatcher(simplePredicate.dimensions(), &mOutputDimensions);
if (mOutputDimensions.size() > 0) {
mSliced = true;
- mDimensionTag = mOutputDimensions[0].mMatcher.getTag();
}
mContainANYPositionInInternalDimensions = HasPositionANY(simplePredicate.dimensions());
}
@@ -95,14 +62,70 @@
bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig,
const vector<sp<ConditionTracker>>& allConditionTrackers,
const unordered_map<int64_t, int>& conditionIdIndexMap,
- vector<bool>& stack,
- vector<ConditionState>& initialConditionCache) {
+ vector<bool>& stack, vector<ConditionState>& conditionCache) {
// SimpleConditionTracker does not have dependency on other conditions, thus we just return
// if the initialization was successful.
- initialConditionCache[mIndex] = mInitialValue;
+ ConditionKey conditionKey;
+ if (mSliced) {
+ conditionKey[mConditionId] = DEFAULT_DIMENSION_KEY;
+ }
+ isConditionMet(conditionKey, allConditionTrackers, mSliced, conditionCache);
return mInitialized;
}
+bool SimpleConditionTracker::onConfigUpdated(
+ const vector<Predicate>& allConditionProtos, const int index,
+ const vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ const unordered_map<int64_t, int>& conditionTrackerMap) {
+ ConditionTracker::onConfigUpdated(allConditionProtos, index, allConditionTrackers,
+ atomMatchingTrackerMap, conditionTrackerMap);
+ setMatcherIndices(allConditionProtos[index].simple_predicate(), atomMatchingTrackerMap);
+ return true;
+}
+
+void SimpleConditionTracker::setMatcherIndices(
+ const SimplePredicate& simplePredicate,
+ const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
+ mTrackerIndex.clear();
+ if (simplePredicate.has_start()) {
+ auto pair = atomMatchingTrackerMap.find(simplePredicate.start());
+ if (pair == atomMatchingTrackerMap.end()) {
+ ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start());
+ return;
+ }
+ mStartLogMatcherIndex = pair->second;
+ mTrackerIndex.insert(mStartLogMatcherIndex);
+ } else {
+ mStartLogMatcherIndex = -1;
+ }
+
+ if (simplePredicate.has_stop()) {
+ auto pair = atomMatchingTrackerMap.find(simplePredicate.stop());
+ if (pair == atomMatchingTrackerMap.end()) {
+ ALOGW("Stop matcher %lld not found in the config", (long long)simplePredicate.stop());
+ return;
+ }
+ mStopLogMatcherIndex = pair->second;
+ mTrackerIndex.insert(mStopLogMatcherIndex);
+ } else {
+ mStopLogMatcherIndex = -1;
+ }
+
+ if (simplePredicate.has_stop_all()) {
+ auto pair = atomMatchingTrackerMap.find(simplePredicate.stop_all());
+ if (pair == atomMatchingTrackerMap.end()) {
+ ALOGW("Stop all matcher %lld found in the config",
+ (long long)simplePredicate.stop_all());
+ return;
+ }
+ mStopAllLogMatcherIndex = pair->second;
+ mTrackerIndex.insert(mStopAllLogMatcherIndex);
+ } else {
+ mStopAllLogMatcherIndex = -1;
+ }
+}
+
void SimpleConditionTracker::dumpState() {
VLOG("%lld DUMP:", (long long)mConditionId);
for (const auto& pair : mSlicedConditionState) {
diff --git a/bin/src/condition/SimpleConditionTracker.h b/bin/src/condition/SimpleConditionTracker.h
index d576b21..4daec06 100644
--- a/bin/src/condition/SimpleConditionTracker.h
+++ b/bin/src/condition/SimpleConditionTracker.h
@@ -27,18 +27,23 @@
namespace os {
namespace statsd {
-class SimpleConditionTracker : public virtual ConditionTracker {
+class SimpleConditionTracker : public ConditionTracker {
public:
- SimpleConditionTracker(const ConfigKey& key, const int64_t& id, const int index,
- const SimplePredicate& simplePredicate,
- const std::unordered_map<int64_t, int>& trackerNameIndexMap);
+ SimpleConditionTracker(const ConfigKey& key, const int64_t& id, const uint64_t protoHash,
+ const int index, const SimplePredicate& simplePredicate,
+ const std::unordered_map<int64_t, int>& atomMatchingTrackerMap);
~SimpleConditionTracker();
bool init(const std::vector<Predicate>& allConditionConfig,
const std::vector<sp<ConditionTracker>>& allConditionTrackers,
const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack,
- std::vector<ConditionState>& initialConditionCache) override;
+ std::vector<ConditionState>& conditionCache) override;
+
+ bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index,
+ const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap) override;
void evaluateCondition(const LogEvent& event,
const std::vector<MatchingState>& eventMatcherValues,
@@ -69,14 +74,9 @@
}
}
- void getTrueSlicedDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions,
- std::set<HashableDimensionKey>* dimensions) const override {
- for (const auto& itr : mSlicedConditionState) {
- if (itr.second > 0) {
- dimensions->insert(itr.first);
- }
- }
+ const std::map<HashableDimensionKey, int>* getSlicedDimensionMap(
+ const std::vector<sp<ConditionTracker>>& allConditions) const override {
+ return &mSlicedConditionState;
}
bool IsChangedDimensionTrackable() const override { return true; }
@@ -112,10 +112,11 @@
std::set<HashableDimensionKey> mLastChangedToTrueDimensions;
std::set<HashableDimensionKey> mLastChangedToFalseDimensions;
- int mDimensionTag;
-
std::map<HashableDimensionKey, int> mSlicedConditionState;
+ void setMatcherIndices(const SimplePredicate& predicate,
+ const std::unordered_map<int64_t, int>& logTrackerMap);
+
void handleStopAll(std::vector<ConditionState>& conditionCache,
std::vector<bool>& changedCache);
@@ -129,6 +130,7 @@
FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedCondition);
FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim);
FRIEND_TEST(SimpleConditionTrackerTest, TestStopAll);
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateConditions);
};
} // namespace statsd
diff --git a/bin/src/external/StatsPullerManager.cpp b/bin/src/external/StatsPullerManager.cpp
index 8a9ec74..8334b6b 100644
--- a/bin/src/external/StatsPullerManager.cpp
+++ b/bin/src/external/StatsPullerManager.cpp
@@ -92,63 +92,43 @@
}
bool StatsPullerManager::Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
- vector<shared_ptr<LogEvent>>* data, bool useUids) {
+ vector<shared_ptr<LogEvent>>* data) {
std::lock_guard<std::mutex> _l(mLock);
- return PullLocked(tagId, configKey, eventTimeNs, data, useUids);
+ return PullLocked(tagId, configKey, eventTimeNs, data);
}
bool StatsPullerManager::Pull(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool useUids) {
+ vector<std::shared_ptr<LogEvent>>* data) {
std::lock_guard<std::mutex> _l(mLock);
- return PullLocked(tagId, uids, eventTimeNs, data, useUids);
+ return PullLocked(tagId, uids, eventTimeNs, data);
}
bool StatsPullerManager::PullLocked(int tagId, const ConfigKey& configKey,
- const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data,
- bool useUids) {
+ const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data) {
vector<int32_t> uids;
- if (useUids) {
- auto uidProviderIt = mPullUidProviders.find(configKey);
- if (uidProviderIt == mPullUidProviders.end()) {
- ALOGE("Error pulling tag %d. No pull uid provider for config key %s", tagId,
- configKey.ToString().c_str());
- StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
- return false;
- }
- sp<PullUidProvider> pullUidProvider = uidProviderIt->second.promote();
- if (pullUidProvider == nullptr) {
- ALOGE("Error pulling tag %d, pull uid provider for config %s is gone.", tagId,
- configKey.ToString().c_str());
- StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
- return false;
- }
- uids = pullUidProvider->getPullAtomUids(tagId);
+ const auto& uidProviderIt = mPullUidProviders.find(configKey);
+ if (uidProviderIt == mPullUidProviders.end()) {
+ ALOGE("Error pulling tag %d. No pull uid provider for config key %s", tagId,
+ configKey.ToString().c_str());
+ StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
+ return false;
}
- return PullLocked(tagId, uids, eventTimeNs, data, useUids);
+ sp<PullUidProvider> pullUidProvider = uidProviderIt->second.promote();
+ if (pullUidProvider == nullptr) {
+ ALOGE("Error pulling tag %d, pull uid provider for config %s is gone.", tagId,
+ configKey.ToString().c_str());
+ StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
+ return false;
+ }
+ uids = pullUidProvider->getPullAtomUids(tagId);
+ return PullLocked(tagId, uids, eventTimeNs, data);
}
bool StatsPullerManager::PullLocked(int tagId, const vector<int32_t>& uids,
- const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data,
- bool useUids) {
+ const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data) {
VLOG("Initiating pulling %d", tagId);
- if (useUids) {
- for (int32_t uid : uids) {
- PullerKey key = {.atomTag = tagId, .uid = uid};
- auto pullerIt = kAllPullAtomInfo.find(key);
- if (pullerIt != kAllPullAtomInfo.end()) {
- bool ret = pullerIt->second->Pull(eventTimeNs, data);
- VLOG("pulled %zu items", data->size());
- if (!ret) {
- StatsdStats::getInstance().notePullFailed(tagId);
- }
- return ret;
- }
- }
- StatsdStats::getInstance().notePullerNotFound(tagId);
- ALOGW("StatsPullerManager: Unknown tagId %d", tagId);
- return false; // Return early since we don't know what to pull.
- } else {
- PullerKey key = {.atomTag = tagId, .uid = -1};
+ for (int32_t uid : uids) {
+ PullerKey key = {.atomTag = tagId, .uid = uid};
auto pullerIt = kAllPullAtomInfo.find(key);
if (pullerIt != kAllPullAtomInfo.end()) {
bool ret = pullerIt->second->Pull(eventTimeNs, data);
@@ -158,9 +138,10 @@
}
return ret;
}
- ALOGW("StatsPullerManager: Unknown tagId %d", tagId);
- return false; // Return early since we don't know what to pull.
}
+ StatsdStats::getInstance().notePullerNotFound(tagId);
+ ALOGW("StatsPullerManager: Unknown tagId %d", tagId);
+ return false; // Return early since we don't know what to pull.
}
bool StatsPullerManager::PullerForMatcherExists(int tagId) const {
@@ -334,6 +315,7 @@
}
int StatsPullerManager::ForceClearPullerCache() {
+ std::lock_guard<std::mutex> _l(mLock);
int totalCleared = 0;
for (const auto& pulledAtom : kAllPullAtomInfo) {
totalCleared += pulledAtom.second->ForceClearCache();
@@ -342,6 +324,7 @@
}
int StatsPullerManager::ClearPullerCacheIfNecessary(int64_t timestampNs) {
+ std::lock_guard<std::mutex> _l(mLock);
int totalCleared = 0;
for (const auto& pulledAtom : kAllPullAtomInfo) {
totalCleared += pulledAtom.second->ClearCacheIfNecessary(timestampNs);
@@ -352,8 +335,7 @@
void StatsPullerManager::RegisterPullAtomCallback(const int uid, const int32_t atomTag,
const int64_t coolDownNs, const int64_t timeoutNs,
const vector<int32_t>& additiveFields,
- const shared_ptr<IPullAtomCallback>& callback,
- bool useUid) {
+ const shared_ptr<IPullAtomCallback>& callback) {
std::lock_guard<std::mutex> _l(mLock);
VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag);
@@ -368,16 +350,15 @@
sp<StatsCallbackPuller> puller = new StatsCallbackPuller(atomTag, callback, actualCoolDownNs,
actualTimeoutNs, additiveFields);
- PullerKey key = {.atomTag = atomTag, .uid = useUid ? uid : -1};
+ PullerKey key = {.atomTag = atomTag, .uid = uid};
AIBinder_linkToDeath(callback->asBinder().get(), mPullAtomCallbackDeathRecipient.get(),
new PullAtomCallbackDeathCookie(this, key, puller));
kAllPullAtomInfo[key] = puller;
}
-void StatsPullerManager::UnregisterPullAtomCallback(const int uid, const int32_t atomTag,
- bool useUids) {
+void StatsPullerManager::UnregisterPullAtomCallback(const int uid, const int32_t atomTag) {
std::lock_guard<std::mutex> _l(mLock);
- PullerKey key = {.atomTag = atomTag, .uid = useUids ? uid : -1};
+ PullerKey key = {.atomTag = atomTag, .uid = uid};
if (kAllPullAtomInfo.find(key) != kAllPullAtomInfo.end()) {
StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag,
/*registered=*/false);
diff --git a/bin/src/external/StatsPullerManager.h b/bin/src/external/StatsPullerManager.h
index 194a0f5..489cbdb 100644
--- a/bin/src/external/StatsPullerManager.h
+++ b/bin/src/external/StatsPullerManager.h
@@ -102,11 +102,11 @@
// If the metric wants to make any change to the data, like timestamps, they
// should make a copy as this data may be shared with multiple metrics.
virtual bool Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool useUids = true);
+ vector<std::shared_ptr<LogEvent>>* data);
// Same as above, but directly specify the allowed uids to pull from.
virtual bool Pull(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool useUids = true);
+ vector<std::shared_ptr<LogEvent>>* data);
// Clear pull data cache immediately.
int ForceClearPullerCache();
@@ -118,10 +118,9 @@
void RegisterPullAtomCallback(const int uid, const int32_t atomTag, const int64_t coolDownNs,
const int64_t timeoutNs, const vector<int32_t>& additiveFields,
- const shared_ptr<IPullAtomCallback>& callback,
- bool useUid = true);
+ const shared_ptr<IPullAtomCallback>& callback);
- void UnregisterPullAtomCallback(const int uid, const int32_t atomTag, bool useUids = true);
+ void UnregisterPullAtomCallback(const int uid, const int32_t atomTag);
std::map<const PullerKey, sp<StatsPuller>> kAllPullAtomInfo;
@@ -153,10 +152,10 @@
std::map<ConfigKey, wp<PullUidProvider>> mPullUidProviders;
bool PullLocked(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool useUids = true);
+ vector<std::shared_ptr<LogEvent>>* data);
bool PullLocked(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool useUids);
+ vector<std::shared_ptr<LogEvent>>* data);
// locks for data receiver and StatsCompanionService changes
std::mutex mLock;
diff --git a/bin/src/flags/flags.cpp b/bin/src/flags/flags.cpp
new file mode 100644
index 0000000..e9fceda
--- /dev/null
+++ b/bin/src/flags/flags.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 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 "flags.h"
+
+#include <server_configurable_flags/get_flags.h>
+
+using server_configurable_flags::GetServerConfigurableFlag;
+using std::string;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+string getFlagString(const string& flagName, const string& defaultValue) {
+ return GetServerConfigurableFlag(STATSD_NATIVE_NAMESPACE, flagName, defaultValue);
+}
+
+bool getFlagBool(const string& flagName, const string& defaultValue) {
+ return getFlagString(flagName, defaultValue) == "true";
+}
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/bin/src/flags/flags.h b/bin/src/flags/flags.h
new file mode 100644
index 0000000..213e1a4
--- /dev/null
+++ b/bin/src/flags/flags.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+
+#include <string>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+const std::string STATSD_NATIVE_NAMESPACE = "statsd_native";
+
+const std::string PARTIAL_CONFIG_UPDATE_FLAG = "partial_config_update";
+
+std::string getFlagString(const std::string& flagName, const std::string& defaultValue);
+
+// Returns true IFF flagName has a value of "true".
+bool getFlagBool(const std::string& flagName, const std::string& defaultValue);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/bin/src/guardrail/StatsdStats.cpp b/bin/src/guardrail/StatsdStats.cpp
index 6e89038..f4e01ce 100644
--- a/bin/src/guardrail/StatsdStats.cpp
+++ b/bin/src/guardrail/StatsdStats.cpp
@@ -521,6 +521,15 @@
getAtomMetricStats(metricId).lateLogEventSkipped++;
}
+void StatsdStats::noteLateLogEvent(int64_t metricId, int64_t extraDurationNs) {
+ lock_guard<std::mutex> lock(mLock);
+ AtomMetricStats& metricStats = getAtomMetricStats(metricId);
+ metricStats.lateLogEvent++;
+ metricStats.sumLateLogEventExtraDurationNs += extraDurationNs;
+ metricStats.maxLateLogEventExtraDurationNs =
+ std::max(metricStats.maxLateLogEventExtraDurationNs, extraDurationNs);
+}
+
void StatsdStats::noteSkippedForwardBuckets(int64_t metricId) {
lock_guard<std::mutex> lock(mLock);
getAtomMetricStats(metricId).skippedForwardBuckets++;
@@ -558,11 +567,11 @@
void StatsdStats::noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayNs) {
lock_guard<std::mutex> lock(mLock);
- AtomMetricStats& pullStats = getAtomMetricStats(metricId);
- pullStats.maxBucketBoundaryDelayNs =
- std::max(pullStats.maxBucketBoundaryDelayNs, timeDelayNs);
- pullStats.minBucketBoundaryDelayNs =
- std::min(pullStats.minBucketBoundaryDelayNs, timeDelayNs);
+ AtomMetricStats& metricStats = getAtomMetricStats(metricId);
+ metricStats.maxBucketBoundaryDelayNs =
+ std::max(metricStats.maxBucketBoundaryDelayNs, timeDelayNs);
+ metricStats.minBucketBoundaryDelayNs =
+ std::min(metricStats.minBucketBoundaryDelayNs, timeDelayNs);
}
void StatsdStats::noteAtomError(int atomTag, bool pull) {
diff --git a/bin/src/guardrail/StatsdStats.h b/bin/src/guardrail/StatsdStats.h
index 0050484..89a51f5 100644
--- a/bin/src/guardrail/StatsdStats.h
+++ b/bin/src/guardrail/StatsdStats.h
@@ -419,6 +419,11 @@
void noteLateLogEventSkipped(int64_t metricId);
/**
+ * A log event was too late, arrived in the wrong bucket.
+ */
+ void noteLateLogEvent(int64_t metricId, int64_t extraDurationNs);
+
+ /**
* Buckets were skipped as time elapsed without any data for them
*/
void noteSkippedForwardBuckets(int64_t metricId);
@@ -542,6 +547,9 @@
int64_t maxBucketBoundaryDelayNs = 0;
long bucketUnknownCondition = 0;
long bucketCount = 0;
+ long lateLogEvent = 0;
+ int64_t sumLateLogEventExtraDurationNs = 0;
+ int64_t maxLateLogEventExtraDurationNs = 0;
} AtomMetricStats;
private:
diff --git a/bin/src/hash.h b/bin/src/hash.h
index cfe869f..bd6b0cd 100644
--- a/bin/src/hash.h
+++ b/bin/src/hash.h
@@ -22,6 +22,7 @@
namespace os {
namespace statsd {
+// Uses murmur2 hashing algorithm.
extern uint32_t Hash32(const char *data, size_t n, uint32_t seed);
extern uint64_t Hash64(const char* data, size_t n, uint64_t seed);
diff --git a/bin/src/main.cpp b/bin/src/main.cpp
index cd9c4e5..03b178a 100644
--- a/bin/src/main.cpp
+++ b/bin/src/main.cpp
@@ -76,7 +76,7 @@
ABinderProcess_startThreadPool();
std::shared_ptr<LogEventQueue> eventQueue =
- std::make_shared<LogEventQueue>(2000 /*buffer limit. Buffer is NOT pre-allocated*/);
+ std::make_shared<LogEventQueue>(4000 /*buffer limit. Buffer is NOT pre-allocated*/);
// Create the service
gStatsService = SharedRefBase::make<StatsService>(looper, eventQueue);
diff --git a/bin/src/matchers/AtomMatchingTracker.h b/bin/src/matchers/AtomMatchingTracker.h
new file mode 100644
index 0000000..0155195
--- /dev/null
+++ b/bin/src/matchers/AtomMatchingTracker.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ATOM_MATCHING_TRACKER_H
+#define ATOM_MATCHING_TRACKER_H
+
+#include "packages/modules/StatsD/bin/src/statsd_config.pb.h"
+#include "logd/LogEvent.h"
+#include "matchers/matcher_util.h"
+
+#include <utils/RefBase.h>
+
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class AtomMatchingTracker : public virtual RefBase {
+public:
+ AtomMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash)
+ : mId(id), mIndex(index), mInitialized(false), mProtoHash(protoHash){};
+
+ virtual ~AtomMatchingTracker(){};
+
+ // Initialize this AtomMatchingTracker.
+ // allAtomMatchers: the list of the AtomMatcher proto config. This is needed because we don't
+ // store the proto object in memory. We only need it during initilization.
+ // allAtomMatchingTrackers: the list of the AtomMatchingTracker objects. It's a one-to-one
+ // mapping with allAtomMatchers. This is needed because the
+ // initialization is done recursively for
+ // CombinationAtomMatchingTrackers using DFS.
+ // stack: a bit map to record which matcher has been visited on the stack. This is for detecting
+ // circle dependency.
+ virtual bool init(const std::vector<AtomMatcher>& allAtomMatchers,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& matcherMap,
+ std::vector<bool>& stack) = 0;
+
+ // Update appropriate state on config updates. Primarily, all indices need to be updated.
+ // This matcher and all of its children are guaranteed to be preserved across the update.
+ // matcher: the AtomMatcher proto from the config.
+ // index: the index of this matcher in mAllAtomMatchingTrackers.
+ // atomMatchingTrackerMap: map from matcher id to index in mAllAtomMatchingTrackers
+ virtual bool onConfigUpdated(
+ const AtomMatcher& matcher, const int index,
+ const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) = 0;
+
+ // Called when a log event comes.
+ // event: the log event.
+ // allAtomMatchingTrackers: the list of all AtomMatchingTrackers. This is needed because the log
+ // processing is done recursively.
+ // matcherResults: The cached results for all matchers for this event. Parent matchers can
+ // directly access the children's matching results if they have been evaluated.
+ // Otherwise, call children matchers' onLogEvent.
+ virtual void onLogEvent(const LogEvent& event,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ std::vector<MatchingState>& matcherResults) = 0;
+
+ // Get the tagIds that this matcher cares about. The combined collection is stored
+ // in MetricMananger, so that we can pass any LogEvents that are not interest of us. It uses
+ // some memory but hopefully it can save us much CPU time when there is flood of events.
+ virtual const std::set<int>& getAtomIds() const {
+ return mAtomIds;
+ }
+
+ int64_t getId() const {
+ return mId;
+ }
+
+ uint64_t getProtoHash() const {
+ return mProtoHash;
+ }
+
+protected:
+ // Name of this matching. We don't really need the name, but it makes log message easy to debug.
+ const int64_t mId;
+
+ // Index of this AtomMatchingTracker in MetricsManager's container.
+ int mIndex;
+
+ // Whether this AtomMatchingTracker has been properly initialized.
+ bool mInitialized;
+
+ // The collection of the event tag ids that this AtomMatchingTracker cares. So we can quickly
+ // return kNotMatched when we receive an event with an id not in the list. This is especially
+ // useful when we have a complex CombinationAtomMatchingTracker.
+ std::set<int> mAtomIds;
+
+ // Hash of the AtomMatcher's proto bytes from StatsdConfig.
+ // Used to determine if the definition of this matcher has changed across a config update.
+ const uint64_t mProtoHash;
+
+ FRIEND_TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerSimple);
+ FRIEND_TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerCombination);
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateMatchers);
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#endif // ATOM_MATCHING_TRACKER_H
diff --git a/bin/src/matchers/CombinationAtomMatchingTracker.cpp b/bin/src/matchers/CombinationAtomMatchingTracker.cpp
new file mode 100644
index 0000000..45685ce
--- /dev/null
+++ b/bin/src/matchers/CombinationAtomMatchingTracker.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2017 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 "Log.h"
+
+#include "CombinationAtomMatchingTracker.h"
+
+#include "matchers/matcher_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+CombinationAtomMatchingTracker::CombinationAtomMatchingTracker(const int64_t& id, const int index,
+ const uint64_t protoHash)
+ : AtomMatchingTracker(id, index, protoHash) {
+}
+
+CombinationAtomMatchingTracker::~CombinationAtomMatchingTracker() {
+}
+
+bool CombinationAtomMatchingTracker::init(
+ const vector<AtomMatcher>& allAtomMatchers,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& matcherMap, vector<bool>& stack) {
+ if (mInitialized) {
+ return true;
+ }
+
+ // mark this node as visited in the recursion stack.
+ stack[mIndex] = true;
+
+ AtomMatcher_Combination matcher = allAtomMatchers[mIndex].combination();
+
+ // LogicalOperation is missing in the config
+ if (!matcher.has_operation()) {
+ return false;
+ }
+
+ mLogicalOperation = matcher.operation();
+
+ if (mLogicalOperation == LogicalOperation::NOT && matcher.matcher_size() != 1) {
+ return false;
+ }
+
+ for (const auto& child : matcher.matcher()) {
+ auto pair = matcherMap.find(child);
+ if (pair == matcherMap.end()) {
+ ALOGW("Matcher %lld not found in the config", (long long)child);
+ return false;
+ }
+
+ int childIndex = pair->second;
+
+ // if the child is a visited node in the recursion -> circle detected.
+ if (stack[childIndex]) {
+ ALOGE("Circle detected in matcher config");
+ return false;
+ }
+
+ if (!allAtomMatchingTrackers[childIndex]->init(allAtomMatchers, allAtomMatchingTrackers,
+ matcherMap, stack)) {
+ ALOGW("child matcher init failed %lld", (long long)child);
+ return false;
+ }
+
+ mChildren.push_back(childIndex);
+
+ const set<int>& childTagIds = allAtomMatchingTrackers[childIndex]->getAtomIds();
+ mAtomIds.insert(childTagIds.begin(), childTagIds.end());
+ }
+
+ mInitialized = true;
+ // unmark this node in the recursion stack.
+ stack[mIndex] = false;
+ return true;
+}
+
+bool CombinationAtomMatchingTracker::onConfigUpdated(
+ const AtomMatcher& matcher, const int index,
+ const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
+ mIndex = index;
+ mChildren.clear();
+ AtomMatcher_Combination combinationMatcher = matcher.combination();
+ for (const int64_t child : combinationMatcher.matcher()) {
+ const auto& pair = atomMatchingTrackerMap.find(child);
+ if (pair == atomMatchingTrackerMap.end()) {
+ ALOGW("Matcher %lld not found in the config", (long long)child);
+ return false;
+ }
+ mChildren.push_back(pair->second);
+ }
+ return true;
+}
+
+void CombinationAtomMatchingTracker::onLogEvent(
+ const LogEvent& event, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ vector<MatchingState>& matcherResults) {
+ // this event has been processed.
+ if (matcherResults[mIndex] != MatchingState::kNotComputed) {
+ return;
+ }
+
+ if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) {
+ matcherResults[mIndex] = MatchingState::kNotMatched;
+ return;
+ }
+
+ // evaluate children matchers if they haven't been evaluated.
+ for (const int childIndex : mChildren) {
+ if (matcherResults[childIndex] == MatchingState::kNotComputed) {
+ const sp<AtomMatchingTracker>& child = allAtomMatchingTrackers[childIndex];
+ child->onLogEvent(event, allAtomMatchingTrackers, matcherResults);
+ }
+ }
+
+ bool matched = combinationMatch(mChildren, mLogicalOperation, matcherResults);
+ matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/bin/src/matchers/CombinationAtomMatchingTracker.h b/bin/src/matchers/CombinationAtomMatchingTracker.h
new file mode 100644
index 0000000..d6e8f2c
--- /dev/null
+++ b/bin/src/matchers/CombinationAtomMatchingTracker.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+#ifndef COMBINATION_ATOM_MATCHING_TRACKER_H
+#define COMBINATION_ATOM_MATCHING_TRACKER_H
+
+#include <unordered_map>
+#include <vector>
+
+#include "AtomMatchingTracker.h"
+#include "packages/modules/StatsD/bin/src/statsd_config.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Represents a AtomMatcher_Combination in the StatsdConfig.
+class CombinationAtomMatchingTracker : public AtomMatchingTracker {
+public:
+ CombinationAtomMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash);
+
+ bool init(const std::vector<AtomMatcher>& allAtomMatchers,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& matcherMap, std::vector<bool>& stack);
+
+ bool onConfigUpdated(const AtomMatcher& matcher, const int index,
+ const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) override;
+
+ ~CombinationAtomMatchingTracker();
+
+ void onLogEvent(const LogEvent& event,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ std::vector<MatchingState>& matcherResults) override;
+
+private:
+ LogicalOperation mLogicalOperation;
+
+ std::vector<int> mChildren;
+
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateMatchers);
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#endif // COMBINATION_ATOM_MATCHING_TRACKER_H
diff --git a/bin/src/matchers/CombinationLogMatchingTracker.cpp b/bin/src/matchers/CombinationLogMatchingTracker.cpp
deleted file mode 100644
index b94a957..0000000
--- a/bin/src/matchers/CombinationLogMatchingTracker.cpp
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2017 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 "Log.h"
-
-#include "CombinationLogMatchingTracker.h"
-#include "matchers/matcher_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::set;
-using std::unordered_map;
-using std::vector;
-
-CombinationLogMatchingTracker::CombinationLogMatchingTracker(const int64_t& id, const int index)
- : LogMatchingTracker(id, index) {
-}
-
-CombinationLogMatchingTracker::~CombinationLogMatchingTracker() {
-}
-
-bool CombinationLogMatchingTracker::init(const vector<AtomMatcher>& allLogMatchers,
- const vector<sp<LogMatchingTracker>>& allTrackers,
- const unordered_map<int64_t, int>& matcherMap,
- vector<bool>& stack) {
- if (mInitialized) {
- return true;
- }
-
- // mark this node as visited in the recursion stack.
- stack[mIndex] = true;
-
- AtomMatcher_Combination matcher = allLogMatchers[mIndex].combination();
-
- // LogicalOperation is missing in the config
- if (!matcher.has_operation()) {
- return false;
- }
-
- mLogicalOperation = matcher.operation();
-
- if (mLogicalOperation == LogicalOperation::NOT && matcher.matcher_size() != 1) {
- return false;
- }
-
- for (const auto& child : matcher.matcher()) {
- auto pair = matcherMap.find(child);
- if (pair == matcherMap.end()) {
- ALOGW("Matcher %lld not found in the config", (long long)child);
- return false;
- }
-
- int childIndex = pair->second;
-
- // if the child is a visited node in the recursion -> circle detected.
- if (stack[childIndex]) {
- ALOGE("Circle detected in matcher config");
- return false;
- }
-
- if (!allTrackers[childIndex]->init(allLogMatchers, allTrackers, matcherMap, stack)) {
- ALOGW("child matcher init failed %lld", (long long)child);
- return false;
- }
-
- mChildren.push_back(childIndex);
-
- const set<int>& childTagIds = allTrackers[childIndex]->getAtomIds();
- mAtomIds.insert(childTagIds.begin(), childTagIds.end());
- }
-
- mInitialized = true;
- // unmark this node in the recursion stack.
- stack[mIndex] = false;
- return true;
-}
-
-void CombinationLogMatchingTracker::onLogEvent(const LogEvent& event,
- const vector<sp<LogMatchingTracker>>& allTrackers,
- vector<MatchingState>& matcherResults) {
- // this event has been processed.
- if (matcherResults[mIndex] != MatchingState::kNotComputed) {
- return;
- }
-
- if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) {
- matcherResults[mIndex] = MatchingState::kNotMatched;
- return;
- }
-
- // evaluate children matchers if they haven't been evaluated.
- for (const int childIndex : mChildren) {
- if (matcherResults[childIndex] == MatchingState::kNotComputed) {
- const sp<LogMatchingTracker>& child = allTrackers[childIndex];
- child->onLogEvent(event, allTrackers, matcherResults);
- }
- }
-
- bool matched = combinationMatch(mChildren, mLogicalOperation, matcherResults);
- matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/bin/src/matchers/CombinationLogMatchingTracker.h b/bin/src/matchers/CombinationLogMatchingTracker.h
deleted file mode 100644
index e949215..0000000
--- a/bin/src/matchers/CombinationLogMatchingTracker.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-#ifndef COMBINATION_LOG_MATCHING_TRACKER_H
-#define COMBINATION_LOG_MATCHING_TRACKER_H
-
-#include <unordered_map>
-#include <vector>
-#include "LogMatchingTracker.h"
-#include "packages/modules/StatsD/bin/src/statsd_config.pb.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// Represents a AtomMatcher_Combination in the StatsdConfig.
-class CombinationLogMatchingTracker : public virtual LogMatchingTracker {
-public:
- CombinationLogMatchingTracker(const int64_t& id, const int index);
-
- bool init(const std::vector<AtomMatcher>& allLogMatchers,
- const std::vector<sp<LogMatchingTracker>>& allTrackers,
- const std::unordered_map<int64_t, int>& matcherMap,
- std::vector<bool>& stack);
-
- ~CombinationLogMatchingTracker();
-
- void onLogEvent(const LogEvent& event,
- const std::vector<sp<LogMatchingTracker>>& allTrackers,
- std::vector<MatchingState>& matcherResults) override;
-
-private:
- LogicalOperation mLogicalOperation;
-
- std::vector<int> mChildren;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#endif // COMBINATION_LOG_MATCHING_TRACKER_H
diff --git a/bin/src/matchers/EventMatcherWizard.h b/bin/src/matchers/EventMatcherWizard.h
index 57ec2b3..5d780f2 100644
--- a/bin/src/matchers/EventMatcherWizard.h
+++ b/bin/src/matchers/EventMatcherWizard.h
@@ -16,7 +16,7 @@
#pragma once
-#include "LogMatchingTracker.h"
+#include "AtomMatchingTracker.h"
namespace android {
namespace os {
@@ -25,7 +25,7 @@
class EventMatcherWizard : public virtual android::RefBase {
public:
EventMatcherWizard(){}; // for testing
- EventMatcherWizard(const std::vector<sp<LogMatchingTracker>>& eventTrackers)
+ EventMatcherWizard(const std::vector<sp<AtomMatchingTracker>>& eventTrackers)
: mAllEventMatchers(eventTrackers){};
virtual ~EventMatcherWizard(){};
@@ -33,7 +33,7 @@
MatchingState matchLogEvent(const LogEvent& event, int matcher_index);
private:
- std::vector<sp<LogMatchingTracker>> mAllEventMatchers;
+ std::vector<sp<AtomMatchingTracker>> mAllEventMatchers;
};
} // namespace statsd
diff --git a/bin/src/matchers/LogMatchingTracker.h b/bin/src/matchers/LogMatchingTracker.h
deleted file mode 100644
index 77f0b99..0000000
--- a/bin/src/matchers/LogMatchingTracker.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#ifndef LOG_MATCHING_TRACKER_H
-#define LOG_MATCHING_TRACKER_H
-
-#include "packages/modules/StatsD/bin/src/statsd_config.pb.h"
-#include "logd/LogEvent.h"
-#include "matchers/matcher_util.h"
-
-#include <utils/RefBase.h>
-
-#include <set>
-#include <unordered_map>
-#include <vector>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class LogMatchingTracker : public virtual RefBase {
-public:
- LogMatchingTracker(const int64_t& id, const int index)
- : mId(id), mIndex(index), mInitialized(false){};
-
- virtual ~LogMatchingTracker(){};
-
- // Initialize this LogMatchingTracker.
- // allLogMatchers: the list of the AtomMatcher proto config. This is needed because we don't
- // store the proto object in memory. We only need it during initilization.
- // allTrackers: the list of the LogMatchingTracker objects. It's a one-to-one mapping with
- // allLogMatchers. This is needed because the initialization is done recursively
- // for CombinationLogMatchingTrackers using DFS.
- // stack: a bit map to record which matcher has been visited on the stack. This is for detecting
- // circle dependency.
- virtual bool init(const std::vector<AtomMatcher>& allLogMatchers,
- const std::vector<sp<LogMatchingTracker>>& allTrackers,
- const std::unordered_map<int64_t, int>& matcherMap,
- std::vector<bool>& stack) = 0;
-
- // Called when a log event comes.
- // event: the log event.
- // allTrackers: the list of all LogMatchingTrackers. This is needed because the log processing
- // is done recursively.
- // matcherResults: The cached results for all matchers for this event. Parent matchers can
- // directly access the children's matching results if they have been evaluated.
- // Otherwise, call children matchers' onLogEvent.
- virtual void onLogEvent(const LogEvent& event,
- const std::vector<sp<LogMatchingTracker>>& allTrackers,
- std::vector<MatchingState>& matcherResults) = 0;
-
- // Get the tagIds that this matcher cares about. The combined collection is stored
- // in MetricMananger, so that we can pass any LogEvents that are not interest of us. It uses
- // some memory but hopefully it can save us much CPU time when there is flood of events.
- virtual const std::set<int>& getAtomIds() const {
- return mAtomIds;
- }
-
- const int64_t& getId() const {
- return mId;
- }
-
-protected:
- // Name of this matching. We don't really need the name, but it makes log message easy to debug.
- const int64_t mId;
-
- // Index of this LogMatchingTracker in MetricsManager's container.
- const int mIndex;
-
- // Whether this LogMatchingTracker has been properly initialized.
- bool mInitialized;
-
- // The collection of the event tag ids that this LogMatchingTracker cares. So we can quickly
- // return kNotMatched when we receive an event with an id not in the list. This is especially
- // useful when we have a complex CombinationLogMatcherTracker.
- std::set<int> mAtomIds;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-
-#endif // LOG_MATCHING_TRACKER_H
diff --git a/bin/src/matchers/SimpleAtomMatchingTracker.cpp b/bin/src/matchers/SimpleAtomMatchingTracker.cpp
new file mode 100644
index 0000000..423da5b
--- /dev/null
+++ b/bin/src/matchers/SimpleAtomMatchingTracker.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define DEBUG false // STOPSHIP if true
+#include "Log.h"
+
+#include "SimpleAtomMatchingTracker.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::unordered_map;
+using std::vector;
+
+SimpleAtomMatchingTracker::SimpleAtomMatchingTracker(const int64_t& id, const int index,
+ const uint64_t protoHash,
+ const SimpleAtomMatcher& matcher,
+ const sp<UidMap>& uidMap)
+ : AtomMatchingTracker(id, index, protoHash), mMatcher(matcher), mUidMap(uidMap) {
+ if (!matcher.has_atom_id()) {
+ mInitialized = false;
+ } else {
+ mAtomIds.insert(matcher.atom_id());
+ mInitialized = true;
+ }
+}
+
+SimpleAtomMatchingTracker::~SimpleAtomMatchingTracker() {
+}
+
+bool SimpleAtomMatchingTracker::init(const vector<AtomMatcher>& allAtomMatchers,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& matcherMap,
+ vector<bool>& stack) {
+ // no need to do anything.
+ return mInitialized;
+}
+
+bool SimpleAtomMatchingTracker::onConfigUpdated(
+ const AtomMatcher& matcher, const int index,
+ const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
+ mIndex = index;
+ // Do not need to update mMatcher since the matcher must be identical across the update.
+ return mInitialized;
+}
+
+void SimpleAtomMatchingTracker::onLogEvent(
+ const LogEvent& event, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ vector<MatchingState>& matcherResults) {
+ if (matcherResults[mIndex] != MatchingState::kNotComputed) {
+ VLOG("Matcher %lld already evaluated ", (long long)mId);
+ return;
+ }
+
+ if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) {
+ matcherResults[mIndex] = MatchingState::kNotMatched;
+ return;
+ }
+
+ bool matched = matchesSimple(mUidMap, mMatcher, event);
+ matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
+ VLOG("Stats SimpleAtomMatcher %lld matched? %d", (long long)mId, matched);
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/bin/src/matchers/SimpleAtomMatchingTracker.h b/bin/src/matchers/SimpleAtomMatchingTracker.h
new file mode 100644
index 0000000..a76a467
--- /dev/null
+++ b/bin/src/matchers/SimpleAtomMatchingTracker.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef SIMPLE_ATOM_MATCHING_TRACKER_H
+#define SIMPLE_ATOM_MATCHING_TRACKER_H
+
+#include <unordered_map>
+#include <vector>
+
+#include "AtomMatchingTracker.h"
+#include "packages/modules/StatsD/bin/src/statsd_config.pb.h"
+#include "packages/UidMap.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class SimpleAtomMatchingTracker : public AtomMatchingTracker {
+public:
+ SimpleAtomMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash,
+ const SimpleAtomMatcher& matcher, const sp<UidMap>& uidMap);
+
+ ~SimpleAtomMatchingTracker();
+
+ bool init(const std::vector<AtomMatcher>& allAtomMatchers,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& matcherMap,
+ std::vector<bool>& stack) override;
+
+ bool onConfigUpdated(const AtomMatcher& matcher, const int index,
+ const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) override;
+
+ void onLogEvent(const LogEvent& event,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ std::vector<MatchingState>& matcherResults) override;
+
+private:
+ const SimpleAtomMatcher mMatcher;
+ const sp<UidMap> mUidMap;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#endif // SIMPLE_ATOM_MATCHING_TRACKER_H
diff --git a/bin/src/matchers/SimpleLogMatchingTracker.cpp b/bin/src/matchers/SimpleLogMatchingTracker.cpp
deleted file mode 100644
index 082daf5..0000000
--- a/bin/src/matchers/SimpleLogMatchingTracker.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "SimpleLogMatchingTracker.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::unordered_map;
-using std::vector;
-
-
-SimpleLogMatchingTracker::SimpleLogMatchingTracker(const int64_t& id, const int index,
- const SimpleAtomMatcher& matcher,
- const UidMap& uidMap)
- : LogMatchingTracker(id, index), mMatcher(matcher), mUidMap(uidMap) {
- if (!matcher.has_atom_id()) {
- mInitialized = false;
- } else {
- mAtomIds.insert(matcher.atom_id());
- mInitialized = true;
- }
-}
-
-SimpleLogMatchingTracker::~SimpleLogMatchingTracker() {
-}
-
-bool SimpleLogMatchingTracker::init(const vector<AtomMatcher>& allLogMatchers,
- const vector<sp<LogMatchingTracker>>& allTrackers,
- const unordered_map<int64_t, int>& matcherMap,
- vector<bool>& stack) {
- // no need to do anything.
- return mInitialized;
-}
-
-void SimpleLogMatchingTracker::onLogEvent(const LogEvent& event,
- const vector<sp<LogMatchingTracker>>& allTrackers,
- vector<MatchingState>& matcherResults) {
- if (matcherResults[mIndex] != MatchingState::kNotComputed) {
- VLOG("Matcher %lld already evaluated ", (long long)mId);
- return;
- }
-
- if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) {
- matcherResults[mIndex] = MatchingState::kNotMatched;
- return;
- }
-
- bool matched = matchesSimple(mUidMap, mMatcher, event);
- matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
- VLOG("Stats SimpleLogMatcher %lld matched? %d", (long long)mId, matched);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/bin/src/matchers/SimpleLogMatchingTracker.h b/bin/src/matchers/SimpleLogMatchingTracker.h
deleted file mode 100644
index ca9200b..0000000
--- a/bin/src/matchers/SimpleLogMatchingTracker.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#ifndef SIMPLE_LOG_MATCHING_TRACKER_H
-#define SIMPLE_LOG_MATCHING_TRACKER_H
-
-#include <unordered_map>
-#include <vector>
-#include "LogMatchingTracker.h"
-#include "packages/modules/StatsD/bin/src/statsd_config.pb.h"
-#include "packages/UidMap.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class SimpleLogMatchingTracker : public virtual LogMatchingTracker {
-public:
- SimpleLogMatchingTracker(const int64_t& id, const int index,
- const SimpleAtomMatcher& matcher,
- const UidMap& uidMap);
-
- ~SimpleLogMatchingTracker();
-
- bool init(const std::vector<AtomMatcher>& allLogMatchers,
- const std::vector<sp<LogMatchingTracker>>& allTrackers,
- const std::unordered_map<int64_t, int>& matcherMap,
- std::vector<bool>& stack) override;
-
- void onLogEvent(const LogEvent& event,
- const std::vector<sp<LogMatchingTracker>>& allTrackers,
- std::vector<MatchingState>& matcherResults) override;
-
-private:
- const SimpleAtomMatcher mMatcher;
- const UidMap& mUidMap;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#endif // SIMPLE_LOG_MATCHING_TRACKER_H
diff --git a/bin/src/matchers/matcher_util.cpp b/bin/src/matchers/matcher_util.cpp
index 6b2fdfd..625eef9 100644
--- a/bin/src/matchers/matcher_util.cpp
+++ b/bin/src/matchers/matcher_util.cpp
@@ -17,7 +17,7 @@
#include "Log.h"
#include "packages/modules/StatsD/bin/src/statsd_config.pb.h"
-#include "matchers/LogMatchingTracker.h"
+#include "matchers/AtomMatchingTracker.h"
#include "matchers/matcher_util.h"
#include "stats_util.h"
@@ -81,14 +81,15 @@
return matched;
}
-bool tryMatchString(const UidMap& uidMap, const FieldValue& fieldValue, const string& str_match) {
+bool tryMatchString(const sp<UidMap>& uidMap, const FieldValue& fieldValue,
+ const string& str_match) {
if (isAttributionUidField(fieldValue) || isUidField(fieldValue)) {
int uid = fieldValue.mValue.int_value;
auto aidIt = UidMap::sAidToUidMapping.find(str_match);
if (aidIt != UidMap::sAidToUidMapping.end()) {
return ((int)aidIt->second) == uid;
}
- std::set<string> packageNames = uidMap.getAppNamesFromUid(uid, true /* normalize*/);
+ std::set<string> packageNames = uidMap->getAppNamesFromUid(uid, true /* normalize*/);
return packageNames.find(str_match) != packageNames.end();
} else if (fieldValue.mValue.getType() == STRING) {
return fieldValue.mValue.str_value == str_match;
@@ -96,7 +97,7 @@
return false;
}
-bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher,
+bool matchesSimple(const sp<UidMap>& uidMap, const FieldValueMatcher& matcher,
const vector<FieldValue>& values, int start, int end, int depth) {
if (depth > 2) {
ALOGE("Depth > 3 not supported");
@@ -353,7 +354,7 @@
}
}
-bool matchesSimple(const UidMap& uidMap, const SimpleAtomMatcher& simpleMatcher,
+bool matchesSimple(const sp<UidMap>& uidMap, const SimpleAtomMatcher& simpleMatcher,
const LogEvent& event) {
if (event.GetTagId() != simpleMatcher.atom_id()) {
return false;
diff --git a/bin/src/matchers/matcher_util.h b/bin/src/matchers/matcher_util.h
index 7493d5d..fc526da 100644
--- a/bin/src/matchers/matcher_util.h
+++ b/bin/src/matchers/matcher_util.h
@@ -36,8 +36,8 @@
bool combinationMatch(const std::vector<int>& children, const LogicalOperation& operation,
const std::vector<MatchingState>& matcherResults);
-bool matchesSimple(const UidMap& uidMap,
- const SimpleAtomMatcher& simpleMatcher, const LogEvent& wrapper);
+bool matchesSimple(const sp<UidMap>& uidMap, const SimpleAtomMatcher& simpleMatcher,
+ const LogEvent& wrapper);
} // namespace statsd
} // namespace os
diff --git a/bin/src/metrics/CountMetricProducer.cpp b/bin/src/metrics/CountMetricProducer.cpp
index 5739612..a8ef54a 100644
--- a/bin/src/metrics/CountMetricProducer.cpp
+++ b/bin/src/metrics/CountMetricProducer.cpp
@@ -24,6 +24,7 @@
#include <stdlib.h>
#include "guardrail/StatsdStats.h"
+#include "metrics/parsing_utils/metrics_manager_util.h"
#include "stats_log_util.h"
#include "stats_util.h"
@@ -69,13 +70,14 @@
CountMetricProducer::CountMetricProducer(
const ConfigKey& key, const CountMetric& metric, const int conditionIndex,
const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
- const int64_t timeBaseNs, const int64_t startTimeNs,
+ const uint64_t protoHash, const int64_t timeBaseNs, const int64_t startTimeNs,
const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
const vector<int>& slicedStateAtoms,
const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
: MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard,
- eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap) {
+ protoHash, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
+ stateGroupMap) {
if (metric.has_bucket()) {
mBucketSizeNs =
TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
@@ -121,6 +123,47 @@
VLOG("~CountMetricProducer() called");
}
+bool CountMetricProducer::onConfigUpdatedLocked(
+ const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
+ if (!MetricProducer::onConfigUpdatedLocked(
+ config, configIndex, metricIndex, allAtomMatchingTrackers,
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+ allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap,
+ trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+ return false;
+ }
+
+ const CountMetric& metric = config.count_metric(configIndex);
+ int trackerIndex;
+ // Update appropriate indices, specifically mConditionIndex and MetricsManager maps.
+ if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false,
+ allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+ trackerToMetricMap, trackerIndex)) {
+ return false;
+ }
+
+ if (metric.has_condition() &&
+ !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+ metric.links(), allConditionTrackers, mConditionTrackerIndex,
+ conditionToMetricMap)) {
+ return false;
+ }
+ return true;
+}
+
void CountMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
const HashableDimensionKey& primaryKey,
const FieldValue& oldState, const FieldValue& newState) {
diff --git a/bin/src/metrics/CountMetricProducer.h b/bin/src/metrics/CountMetricProducer.h
index 5399c34..4b1f1c8 100644
--- a/bin/src/metrics/CountMetricProducer.h
+++ b/bin/src/metrics/CountMetricProducer.h
@@ -44,7 +44,7 @@
CountMetricProducer(
const ConfigKey& key, const CountMetric& countMetric, const int conditionIndex,
const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
- const int64_t timeBaseNs, const int64_t startTimeNs,
+ const uint64_t protoHash, const int64_t timeBaseNs, const int64_t startTimeNs,
const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
eventDeactivationMap = {},
@@ -57,6 +57,10 @@
const HashableDimensionKey& primaryKey, const FieldValue& oldState,
const FieldValue& newState) override;
+ MetricType getMetricType() const override {
+ return METRIC_TYPE_COUNT;
+ }
+
protected:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
@@ -93,6 +97,22 @@
void flushCurrentBucketLocked(const int64_t& eventTimeNs,
const int64_t& nextBucketStartTimeNs) override;
+ bool onConfigUpdatedLocked(
+ const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const sp<ConditionWizard>& wizard,
+ const std::unordered_map<int64_t, int>& metricToActivationMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation) override;
+
std::unordered_map<MetricDimensionKey, std::vector<CountBucket>> mPastBuckets;
// The current bucket (may be a partial bucket).
diff --git a/bin/src/metrics/DurationMetricProducer.cpp b/bin/src/metrics/DurationMetricProducer.cpp
index e9b0438..fe92b08 100644
--- a/bin/src/metrics/DurationMetricProducer.cpp
+++ b/bin/src/metrics/DurationMetricProducer.cpp
@@ -17,14 +17,17 @@
#define DEBUG false
#include "Log.h"
+
#include "DurationMetricProducer.h"
-#include "guardrail/StatsdStats.h"
-#include "stats_util.h"
-#include "stats_log_util.h"
#include <limits.h>
#include <stdlib.h>
+#include "guardrail/StatsdStats.h"
+#include "metrics/parsing_utils/metrics_manager_util.h"
+#include "stats_log_util.h"
+#include "stats_util.h"
+
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_FLOAT;
@@ -64,16 +67,17 @@
DurationMetricProducer::DurationMetricProducer(
const ConfigKey& key, const DurationMetric& metric, const int conditionIndex,
- const vector<ConditionState>& initialConditionCache, const size_t startIndex,
- const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
- const sp<ConditionWizard>& wizard, const FieldMatcher& internalDimensions,
- const int64_t timeBaseNs, const int64_t startTimeNs,
+ const vector<ConditionState>& initialConditionCache, const int whatIndex,
+ const int startIndex, const int stopIndex, const int stopAllIndex, const bool nesting,
+ const sp<ConditionWizard>& wizard, const uint64_t protoHash,
+ const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs,
const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
const vector<int>& slicedStateAtoms,
const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
: MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard,
- eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap),
+ protoHash, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
+ stateGroupMap),
mAggregationType(metric.aggregation_type()),
mStartIndex(startIndex),
mStopIndex(stopIndex),
@@ -103,6 +107,12 @@
ALOGE("Position ANY in dimension_in_what not supported.");
}
+ // Dimensions in what must be subset of internal dimensions
+ if (!subsetDimensions(mDimensionsInWhat, mInternalDimensions)) {
+ ALOGE("Dimensions in what must be a subset of the internal dimensions");
+ mValid = false;
+ }
+
mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what());
if (metric.links().size() > 0) {
@@ -111,6 +121,10 @@
mc.conditionId = link.condition();
translateFieldMatcher(link.fields_in_what(), &mc.metricFields);
translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
+ if (!subsetDimensions(mc.metricFields, mInternalDimensions)) {
+ ALOGE(("Condition links must be a subset of the internal dimensions"));
+ mValid = false;
+ }
mMetric2ConditionLinks.push_back(mc);
}
mConditionSliced = true;
@@ -122,6 +136,10 @@
ms.stateAtomId = stateLink.state_atom_id();
translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields);
translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields);
+ if (!subsetDimensions(ms.metricFields, mInternalDimensions)) {
+ ALOGE(("State links must be a subset of the dimensions in what internal dimensions"));
+ mValid = false;
+ }
mMetric2StateLinks.push_back(ms);
}
@@ -136,12 +154,109 @@
mCurrentBucketStartTimeNs = startTimeNs;
VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
(long long)mBucketSizeNs, (long long)mTimeBaseNs);
+
+ initTrueDimensions(whatIndex, startTimeNs);
}
DurationMetricProducer::~DurationMetricProducer() {
VLOG("~DurationMetric() called");
}
+bool DurationMetricProducer::onConfigUpdatedLocked(
+ const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
+ if (!MetricProducer::onConfigUpdatedLocked(
+ config, configIndex, metricIndex, allAtomMatchingTrackers,
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+ allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap,
+ trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+ return false;
+ }
+
+ const DurationMetric& metric = config.duration_metric(configIndex);
+ const auto& what_it = conditionTrackerMap.find(metric.what());
+ if (what_it == conditionTrackerMap.end()) {
+ ALOGE("DurationMetric's \"what\" is not present in the config");
+ return false;
+ }
+
+ const Predicate& durationWhat = config.predicate(what_it->second);
+ if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) {
+ ALOGE("DurationMetric's \"what\" must be a simple condition");
+ return false;
+ }
+
+ const SimplePredicate& simplePredicate = durationWhat.simple_predicate();
+
+ // Update indices: mStartIndex, mStopIndex, mStopAllIndex, mConditionIndex and MetricsManager
+ // maps.
+ if (!handleMetricWithAtomMatchingTrackers(simplePredicate.start(), metricIndex,
+ metric.has_dimensions_in_what(),
+ allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+ trackerToMetricMap, mStartIndex)) {
+ ALOGE("Duration metrics must specify a valid start event matcher");
+ return false;
+ }
+
+ if (simplePredicate.has_stop() &&
+ !handleMetricWithAtomMatchingTrackers(simplePredicate.stop(), metricIndex,
+ metric.has_dimensions_in_what(),
+ allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+ trackerToMetricMap, mStopIndex)) {
+ return false;
+ }
+
+ if (simplePredicate.has_stop_all() &&
+ !handleMetricWithAtomMatchingTrackers(simplePredicate.stop_all(), metricIndex,
+ metric.has_dimensions_in_what(),
+ allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+ trackerToMetricMap, mStopAllIndex)) {
+ return false;
+ }
+
+ if (metric.has_condition() &&
+ !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+ metric.links(), allConditionTrackers, mConditionTrackerIndex,
+ conditionToMetricMap)) {
+ return false;
+ }
+
+ for (const auto& it : mCurrentSlicedDurationTrackerMap) {
+ it.second->onConfigUpdated(wizard, mConditionTrackerIndex);
+ }
+
+ return true;
+}
+
+void DurationMetricProducer::initTrueDimensions(const int whatIndex, const int64_t startTimeNs) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ // Currently whatIndex will only be -1 in tests. In the future, we might want to avoid creating
+ // a ConditionTracker if the condition is only used in the "what" of a duration metric. In that
+ // scenario, -1 can also be passed.
+ if (whatIndex == -1) {
+ return;
+ }
+ const map<HashableDimensionKey, int>* slicedWhatMap = mWizard->getSlicedDimensionMap(whatIndex);
+ for (const auto& [internalDimKey, count] : *slicedWhatMap) {
+ for (int i = 0; i < count; i++) {
+ // Fake start events.
+ handleMatchedLogEventValuesLocked(mStartIndex, internalDimKey.getValues(), startTimeNs);
+ }
+ }
+}
+
sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker(
const Alert &alert, const sp<AlarmMonitor>& anomalyAlarmMonitor) {
std::lock_guard<std::mutex> lock(mMutex);
@@ -152,14 +267,26 @@
return nullptr;
}
}
- sp<DurationAnomalyTracker> anomalyTracker =
- new DurationAnomalyTracker(alert, mConfigKey, anomalyAlarmMonitor);
- if (anomalyTracker != nullptr) {
- mAnomalyTrackers.push_back(anomalyTracker);
- }
+ sp<AnomalyTracker> anomalyTracker =
+ new DurationAnomalyTracker(alert, mConfigKey, anomalyAlarmMonitor);
+ addAnomalyTrackerLocked(anomalyTracker);
return anomalyTracker;
}
+// Adds an AnomalyTracker that has already been created.
+// Note: this gets called on config updates, and will only get called if the metric and the
+// associated alert are preserved, which means the AnomalyTracker must be a DurationAnomalyTracker.
+void DurationMetricProducer::addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ addAnomalyTrackerLocked(anomalyTracker);
+}
+
+void DurationMetricProducer::addAnomalyTrackerLocked(sp<AnomalyTracker>& anomalyTracker) {
+ mAnomalyTrackers.push_back(anomalyTracker);
+ for (const auto& [_, durationTracker] : mCurrentSlicedDurationTrackerMap) {
+ durationTracker->addAnomalyTracker(anomalyTracker);
+ }
+}
void DurationMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
const HashableDimensionKey& primaryKey,
const FieldValue& oldState,
@@ -171,6 +298,12 @@
flushIfNeededLocked(eventTimeNs);
+ // Log late event and extra duration.
+ if (eventTimeNs < mCurrentBucketStartTimeNs) {
+ StatsdStats::getInstance().noteLateLogEvent(mMetricId,
+ mCurrentBucketStartTimeNs - eventTimeNs);
+ }
+
// Each duration tracker is mapped to a different whatKey (a set of values from the
// dimensionsInWhat fields). We notify all trackers iff the primaryKey field values from the
// state change event are a subset of the tracker's whatKey field values.
@@ -236,14 +369,14 @@
// state based on the new unsliced condition state.
if (dimensionsChangedToTrue == nullptr || dimensionsChangedToFalse == nullptr ||
(dimensionsChangedToTrue->empty() && dimensionsChangedToFalse->empty())) {
- std::set<HashableDimensionKey> trueConditionDimensions;
- mWizard->getTrueSlicedDimensions(mConditionTrackerIndex, &trueConditionDimensions);
+ const map<HashableDimensionKey, int>* slicedConditionMap =
+ mWizard->getSlicedDimensionMap(mConditionTrackerIndex);
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
HashableDimensionKey linkedConditionDimensionKey;
getDimensionForCondition(whatIt.first.getValues(), mMetric2ConditionLinks[0],
&linkedConditionDimensionKey);
- if (trueConditionDimensions.find(linkedConditionDimensionKey) !=
- trueConditionDimensions.end()) {
+ const auto& slicedConditionIt = slicedConditionMap->find(linkedConditionDimensionKey);
+ if (slicedConditionIt != slicedConditionMap->end() && slicedConditionIt->second > 0) {
whatIt.second->onConditionChanged(currentUnSlicedPartCondition, eventTime);
}
}
@@ -289,6 +422,12 @@
return;
}
+ // Log late event and extra duration.
+ if (eventTime < mCurrentBucketStartTimeNs) {
+ StatsdStats::getInstance().noteLateLogEvent(mMetricId,
+ mCurrentBucketStartTimeNs - eventTime);
+ }
+
flushIfNeededLocked(eventTime);
if (!mConditionSliced) {
@@ -306,6 +445,12 @@
return;
}
+ // Log late event and extra duration.
+ if (eventTimeNs < mCurrentBucketStartTimeNs) {
+ StatsdStats::getInstance().noteLateLogEvent(mMetricId,
+ mCurrentBucketStartTimeNs - eventTimeNs);
+ }
+
if (mIsActive) {
flushIfNeededLocked(eventTimeNs);
}
@@ -332,6 +477,12 @@
return;
}
+ // Log late event and extra duration.
+ if (eventTime < mCurrentBucketStartTimeNs) {
+ StatsdStats::getInstance().noteLateLogEvent(mMetricId,
+ mCurrentBucketStartTimeNs - eventTime);
+ }
+
flushIfNeededLocked(eventTime);
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
whatIt.second->onConditionChanged(conditionMet, eventTime);
@@ -501,8 +652,9 @@
}
void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKeys,
- bool condition, const LogEvent& event) {
+ const ConditionKey& conditionKeys, bool condition,
+ const int64_t eventTimeNs,
+ const vector<FieldValue>& eventValues) {
const auto& whatKey = eventKey.getDimensionKeyInWhat();
auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey);
if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
@@ -514,20 +666,17 @@
auto it = mCurrentSlicedDurationTrackerMap.find(whatKey);
if (mUseWhatDimensionAsInternalDimension) {
- it->second->noteStart(whatKey, condition, event.GetElapsedTimestampNs(), conditionKeys);
+ it->second->noteStart(whatKey, condition, eventTimeNs, conditionKeys);
return;
}
if (mInternalDimensions.empty()) {
- it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, event.GetElapsedTimestampNs(),
- conditionKeys);
+ it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, eventTimeNs, conditionKeys);
} else {
HashableDimensionKey dimensionKey = DEFAULT_DIMENSION_KEY;
- filterValues(mInternalDimensions, event.getValues(), &dimensionKey);
- it->second->noteStart(dimensionKey, condition, event.GetElapsedTimestampNs(),
- conditionKeys);
+ filterValues(mInternalDimensions, eventValues, &dimensionKey);
+ it->second->noteStart(dimensionKey, condition, eventTimeNs, conditionKeys);
}
-
}
void DurationMetricProducer::onMatchedLogEventInternalLocked(
@@ -539,26 +688,38 @@
void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
const LogEvent& event) {
- int64_t eventTimeNs = event.GetElapsedTimestampNs();
+ handleMatchedLogEventValuesLocked(matcherIndex, event.getValues(),
+ event.GetElapsedTimestampNs());
+}
+
+void DurationMetricProducer::handleMatchedLogEventValuesLocked(const size_t matcherIndex,
+ const vector<FieldValue>& values,
+ const int64_t eventTimeNs) {
if (eventTimeNs < mTimeBaseNs) {
return;
}
+ // Log late event and extra duration.
+ if (eventTimeNs < mCurrentBucketStartTimeNs) {
+ StatsdStats::getInstance().noteLateLogEvent(mMetricId,
+ mCurrentBucketStartTimeNs - eventTimeNs);
+ }
+
if (mIsActive) {
- flushIfNeededLocked(event.GetElapsedTimestampNs());
+ flushIfNeededLocked(eventTimeNs);
}
// Handles Stopall events.
- if (matcherIndex == mStopAllIndex) {
+ if ((int)matcherIndex == mStopAllIndex) {
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- whatIt.second->noteStopAll(event.GetElapsedTimestampNs());
+ whatIt.second->noteStopAll(eventTimeNs);
}
return;
}
HashableDimensionKey dimensionInWhat = DEFAULT_DIMENSION_KEY;
if (!mDimensionsInWhat.empty()) {
- filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
+ filterValues(mDimensionsInWhat, values, &dimensionInWhat);
}
// Stores atom id to primary key pairs for each state atom that the metric is
@@ -569,8 +730,7 @@
// field values from the log event. These values will form a primary key
// that will be used to query StateTracker for the correct state value.
for (const auto& stateLink : mMetric2StateLinks) {
- getDimensionForState(event.getValues(), stateLink,
- &statePrimaryKeys[stateLink.stateAtomId]);
+ getDimensionForState(values, stateLink, &statePrimaryKeys[stateLink.stateAtomId]);
}
// For each sliced state, query StateTracker for the state value using
@@ -597,23 +757,23 @@
}
// Handles Stop events.
- if (matcherIndex == mStopIndex) {
+ if ((int)matcherIndex == mStopIndex) {
if (mUseWhatDimensionAsInternalDimension) {
auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
- whatIt->second->noteStop(dimensionInWhat, event.GetElapsedTimestampNs(), false);
+ whatIt->second->noteStop(dimensionInWhat, eventTimeNs, false);
}
return;
}
HashableDimensionKey internalDimensionKey = DEFAULT_DIMENSION_KEY;
if (!mInternalDimensions.empty()) {
- filterValues(mInternalDimensions, event.getValues(), &internalDimensionKey);
+ filterValues(mInternalDimensions, values, &internalDimensionKey);
}
auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
- whatIt->second->noteStop(internalDimensionKey, event.GetElapsedTimestampNs(), false);
+ whatIt->second->noteStop(internalDimensionKey, eventTimeNs, false);
}
return;
}
@@ -622,7 +782,7 @@
ConditionKey conditionKey;
if (mConditionSliced) {
for (const auto& link : mMetric2ConditionLinks) {
- getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
+ getDimensionForCondition(values, link, &conditionKey[link.conditionId]);
}
auto conditionState =
@@ -637,7 +797,7 @@
condition = condition && mIsActive;
handleStartEvent(MetricDimensionKey(dimensionInWhat, stateValuesKey), conditionKey, condition,
- event);
+ eventTimeNs, values);
}
size_t DurationMetricProducer::byteSizeLocked() const {
diff --git a/bin/src/metrics/DurationMetricProducer.h b/bin/src/metrics/DurationMetricProducer.h
index b46fb1f..5d3cd11 100644
--- a/bin/src/metrics/DurationMetricProducer.h
+++ b/bin/src/metrics/DurationMetricProducer.h
@@ -40,10 +40,11 @@
public:
DurationMetricProducer(
const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex,
- const vector<ConditionState>& initialConditionCache, const size_t startIndex,
- const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
- const sp<ConditionWizard>& wizard, const FieldMatcher& internalDimensions,
- const int64_t timeBaseNs, const int64_t startTimeNs,
+ const vector<ConditionState>& initialConditionCache, const int whatIndex,
+ const int startIndex, const int stopIndex, const int stopAllIndex, const bool nesting,
+ const sp<ConditionWizard>& wizard, const uint64_t protoHash,
+ const FieldMatcher& internalDimensions, const int64_t timeBaseNs,
+ const int64_t startTimeNs,
const unordered_map<int, shared_ptr<Activation>>& eventActivationMap = {},
const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap = {},
const vector<int>& slicedStateAtoms = {},
@@ -54,10 +55,16 @@
sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
const sp<AlarmMonitor>& anomalyAlarmMonitor) override;
+ void addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker) override;
+
void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
const HashableDimensionKey& primaryKey, const FieldValue& oldState,
const FieldValue& newState) override;
+ MetricType getMetricType() const override {
+ return METRIC_TYPE_DURATION;
+ }
+
protected:
void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) override;
@@ -67,8 +74,15 @@
const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
private:
+ // Initializes true dimensions of the 'what' predicate. Only to be called during initialization.
+ void initTrueDimensions(const int whatIndex, const int64_t startTimeNs);
+
+ void handleMatchedLogEventValuesLocked(const size_t matcherIndex,
+ const std::vector<FieldValue>& values,
+ const int64_t eventTimeNs);
void handleStartEvent(const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys,
- bool condition, const LogEvent& event);
+ bool condition, const int64_t eventTimeNs,
+ const vector<FieldValue>& eventValues);
void onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
@@ -107,16 +121,34 @@
void flushCurrentBucketLocked(const int64_t& eventTimeNs,
const int64_t& nextBucketStartTimeNs) override;
+ bool onConfigUpdatedLocked(
+ const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const sp<ConditionWizard>& wizard,
+ const std::unordered_map<int64_t, int>& metricToActivationMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation) override;
+
+ void addAnomalyTrackerLocked(sp<AnomalyTracker>& anomalyTracker);
+
const DurationMetric_AggregationType mAggregationType;
// Index of the SimpleAtomMatcher which defines the start.
- const size_t mStartIndex;
+ int mStartIndex;
// Index of the SimpleAtomMatcher which defines the stop.
- const size_t mStopIndex;
+ int mStopIndex;
// Index of the SimpleAtomMatcher which defines the stop all for all dimensions.
- const size_t mStopAllIndex;
+ int mStopAllIndex;
// nest counting -- for the same key, stops must match the number of starts to make real stop
const bool mNested;
@@ -143,9 +175,6 @@
std::unique_ptr<DurationTracker> createDurationTracker(
const MetricDimensionKey& eventKey) const;
- // This hides the base class's std::vector<sp<AnomalyTracker>> mAnomalyTrackers
- std::vector<sp<DurationAnomalyTracker>> mAnomalyTrackers;
-
// Util function to check whether the specified dimension hits the guardrail.
bool hitGuardRailLocked(const MetricDimensionKey& newKey);
@@ -162,6 +191,9 @@
TestSumDurationWithSplitInFollowingBucket);
FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDuration);
FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextBucket);
+
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics);
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateAlerts);
};
} // namespace statsd
diff --git a/bin/src/metrics/EventMetricProducer.cpp b/bin/src/metrics/EventMetricProducer.cpp
index dc0036a..ca302c0 100644
--- a/bin/src/metrics/EventMetricProducer.cpp
+++ b/bin/src/metrics/EventMetricProducer.cpp
@@ -18,12 +18,14 @@
#include "Log.h"
#include "EventMetricProducer.h"
-#include "stats_util.h"
-#include "stats_log_util.h"
#include <limits.h>
#include <stdlib.h>
+#include "metrics/parsing_utils/metrics_manager_util.h"
+#include "stats_log_util.h"
+#include "stats_util.h"
+
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_FLOAT;
@@ -55,13 +57,14 @@
EventMetricProducer::EventMetricProducer(
const ConfigKey& key, const EventMetric& metric, const int conditionIndex,
const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
- const int64_t startTimeNs,
+ const uint64_t protoHash, const int64_t startTimeNs,
const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
const vector<int>& slicedStateAtoms,
const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
: MetricProducer(metric.id(), key, startTimeNs, conditionIndex, initialConditionCache, wizard,
- eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap) {
+ protoHash, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
+ stateGroupMap) {
if (metric.links().size() > 0) {
for (const auto& link : metric.links()) {
Metric2Condition mc;
@@ -81,6 +84,47 @@
VLOG("~EventMetricProducer() called");
}
+bool EventMetricProducer::onConfigUpdatedLocked(
+ const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
+ if (!MetricProducer::onConfigUpdatedLocked(
+ config, configIndex, metricIndex, allAtomMatchingTrackers,
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+ allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap,
+ trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+ return false;
+ }
+
+ const EventMetric& metric = config.event_metric(configIndex);
+ int trackerIndex;
+ // Update appropriate indices, specifically mConditionIndex and MetricsManager maps.
+ if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false,
+ allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+ trackerToMetricMap, trackerIndex)) {
+ return false;
+ }
+
+ if (metric.has_condition() &&
+ !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+ metric.links(), allConditionTrackers, mConditionTrackerIndex,
+ conditionToMetricMap)) {
+ return false;
+ }
+ return true;
+}
+
void EventMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
mProto->clear();
StatsdStats::getInstance().noteBucketDropped(mMetricId);
diff --git a/bin/src/metrics/EventMetricProducer.h b/bin/src/metrics/EventMetricProducer.h
index f6fbadc..5236ebe 100644
--- a/bin/src/metrics/EventMetricProducer.h
+++ b/bin/src/metrics/EventMetricProducer.h
@@ -36,7 +36,7 @@
EventMetricProducer(
const ConfigKey& key, const EventMetric& eventMetric, const int conditionIndex,
const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
- const int64_t startTimeNs,
+ const uint64_t protoHash, const int64_t startTimeNs,
const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
eventDeactivationMap = {},
@@ -45,6 +45,10 @@
virtual ~EventMetricProducer();
+ MetricType getMetricType() const override {
+ return METRIC_TYPE_EVENT;
+ }
+
private:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
@@ -65,6 +69,22 @@
// Internal interface to handle sliced condition change.
void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
+ bool onConfigUpdatedLocked(
+ const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const sp<ConditionWizard>& wizard,
+ const std::unordered_map<int64_t, int>& metricToActivationMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation) override;
+
void dropDataLocked(const int64_t dropTimeNs) override;
// Internal function to calculate the current used bytes.
diff --git a/bin/src/metrics/GaugeMetricProducer.cpp b/bin/src/metrics/GaugeMetricProducer.cpp
index 020f4b6..2a37b58 100644
--- a/bin/src/metrics/GaugeMetricProducer.cpp
+++ b/bin/src/metrics/GaugeMetricProducer.cpp
@@ -17,9 +17,11 @@
#define DEBUG false // STOPSHIP if true
#include "Log.h"
-#include "../guardrail/StatsdStats.h"
#include "GaugeMetricProducer.h"
-#include "../stats_log_util.h"
+
+#include "guardrail/StatsdStats.h"
+#include "metrics/parsing_utils/metrics_manager_util.h"
+#include "stats_log_util.h"
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
@@ -71,13 +73,14 @@
GaugeMetricProducer::GaugeMetricProducer(
const ConfigKey& key, const GaugeMetric& metric, const int conditionIndex,
const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
- const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard,
- const int pullTagId, const int triggerAtomId, const int atomId, const int64_t timeBaseNs,
- const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const uint64_t protoHash, const int whatMatcherIndex,
+ const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int triggerAtomId,
+ const int atomId, const int64_t timeBaseNs, const int64_t startTimeNs,
+ const sp<StatsPullerManager>& pullerManager,
const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap)
: MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard,
- eventActivationMap, eventDeactivationMap, /*slicedStateAtoms=*/{},
+ protoHash, eventActivationMap, eventDeactivationMap, /*slicedStateAtoms=*/{},
/*stateGroupMap=*/{}),
mWhatMatcherIndex(whatMatcherIndex),
mEventMatcherWizard(matcherWizard),
@@ -153,6 +156,58 @@
}
}
+bool GaugeMetricProducer::onConfigUpdatedLocked(
+ const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
+ if (!MetricProducer::onConfigUpdatedLocked(
+ config, configIndex, metricIndex, allAtomMatchingTrackers,
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+ allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap,
+ trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+ return false;
+ }
+
+ const GaugeMetric& metric = config.gauge_metric(configIndex);
+ // Update appropriate indices: mWhatMatcherIndex, mConditionIndex and MetricsManager maps.
+ if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, /*enforceOneAtom=*/false,
+ allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+ trackerToMetricMap, mWhatMatcherIndex)) {
+ return false;
+ }
+
+ // Need to update maps since the index changed, but mTriggerAtomId will not change.
+ int triggerTrackerIndex;
+ if (metric.has_trigger_event() &&
+ !handleMetricWithAtomMatchingTrackers(metric.trigger_event(), metricIndex,
+ /*enforceOneAtom=*/true, allAtomMatchingTrackers,
+ newAtomMatchingTrackerMap, trackerToMetricMap,
+ triggerTrackerIndex)) {
+ return false;
+ }
+
+ if (metric.has_condition() &&
+ !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+ metric.links(), allConditionTrackers, mConditionTrackerIndex,
+ conditionToMetricMap)) {
+ return false;
+ }
+ sp<EventMatcherWizard> tmpEventWizard = mEventMatcherWizard;
+ mEventMatcherWizard = matcherWizard;
+ return true;
+}
+
void GaugeMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
if (mCurrentSlicedBucket == nullptr ||
mCurrentSlicedBucket->size() == 0) {
diff --git a/bin/src/metrics/GaugeMetricProducer.h b/bin/src/metrics/GaugeMetricProducer.h
index 5aa6b87..751b487 100644
--- a/bin/src/metrics/GaugeMetricProducer.h
+++ b/bin/src/metrics/GaugeMetricProducer.h
@@ -53,16 +53,17 @@
// This gauge metric producer first register the puller to automatically pull the gauge at the
// beginning of each bucket. If the condition is met, insert it to the bucket info. Otherwise
// proactively pull the gauge when the condition is changed to be true. Therefore, the gauge metric
-// producer always reports the guage at the earliest time of the bucket when the condition is met.
-class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
+// producer always reports the gauge at the earliest time of the bucket when the condition is met.
+class GaugeMetricProducer : public MetricProducer, public virtual PullDataReceiver {
public:
GaugeMetricProducer(
const ConfigKey& key, const GaugeMetric& gaugeMetric, const int conditionIndex,
const vector<ConditionState>& initialConditionCache,
- const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
- const sp<EventMatcherWizard>& matcherWizard, const int pullTagId,
- const int triggerAtomId, const int atomId, const int64_t timeBaseNs,
- const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const sp<ConditionWizard>& conditionWizard, const uint64_t protoHash,
+ const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard,
+ const int pullTagId, const int triggerAtomId, const int atomId,
+ const int64_t timeBaseNs, const int64_t startTimeNs,
+ const sp<StatsPullerManager>& pullerManager,
const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
eventDeactivationMap = {});
@@ -96,6 +97,10 @@
}
};
+ MetricType getMetricType() const override {
+ return METRIC_TYPE_GAUGE;
+ }
+
protected:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
@@ -137,7 +142,23 @@
void pullAndMatchEventsLocked(const int64_t timestampNs);
- const int mWhatMatcherIndex;
+ bool onConfigUpdatedLocked(
+ const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const sp<ConditionWizard>& wizard,
+ const std::unordered_map<int64_t, int>& metricToActivationMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation) override;
+
+ int mWhatMatcherIndex;
sp<EventMatcherWizard> mEventMatcherWizard;
@@ -204,6 +225,8 @@
FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPushedEvents);
FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPulled);
+
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateGaugeMetrics);
};
} // namespace statsd
diff --git a/bin/src/metrics/MetricProducer.cpp b/bin/src/metrics/MetricProducer.cpp
index fe143e4..c68e61e 100644
--- a/bin/src/metrics/MetricProducer.cpp
+++ b/bin/src/metrics/MetricProducer.cpp
@@ -20,6 +20,7 @@
#include "MetricProducer.h"
#include "../guardrail/StatsdStats.h"
+#include "metrics/parsing_utils/metrics_manager_util.h"
#include "state/StateTracker.h"
using android::util::FIELD_COUNT_REPEATED;
@@ -46,14 +47,16 @@
MetricProducer::MetricProducer(
const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
const int conditionIndex, const vector<ConditionState>& initialConditionCache,
- const sp<ConditionWizard>& wizard,
+ const sp<ConditionWizard>& wizard, const uint64_t protoHash,
const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
eventDeactivationMap,
const vector<int>& slicedStateAtoms,
const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
: mMetricId(metricId),
+ mProtoHash(protoHash),
mConfigKey(key),
+ mValid(true),
mTimeBaseNs(timeBaseNs),
mCurrentBucketStartTimeNs(timeBaseNs),
mCurrentBucketNum(0),
@@ -71,6 +74,38 @@
mStateGroupMap(stateGroupMap) {
}
+bool MetricProducer::onConfigUpdatedLocked(
+ const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
+ sp<ConditionWizard> tmpWizard = mWizard;
+ mWizard = wizard;
+
+ unordered_map<int, shared_ptr<Activation>> newEventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> newEventDeactivationMap;
+ if (!handleMetricActivationOnConfigUpdate(
+ config, mMetricId, metricIndex, metricToActivationMap, oldAtomMatchingTrackerMap,
+ newAtomMatchingTrackerMap, mEventActivationMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation, newEventActivationMap,
+ newEventDeactivationMap)) {
+ return false;
+ }
+ mEventActivationMap = newEventActivationMap;
+ mEventDeactivationMap = newEventDeactivationMap;
+ mAnomalyTrackers.clear();
+ return true;
+}
+
void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
if (!mIsActive) {
return;
diff --git a/bin/src/metrics/MetricProducer.h b/bin/src/metrics/MetricProducer.h
index d1ea0a4..9a69028 100644
--- a/bin/src/metrics/MetricProducer.h
+++ b/bin/src/metrics/MetricProducer.h
@@ -26,6 +26,7 @@
#include "anomaly/AnomalyTracker.h"
#include "condition/ConditionWizard.h"
#include "config/ConfigKey.h"
+#include "matchers/EventMatcherWizard.h"
#include "matchers/matcher_util.h"
#include "packages/PackageInfoListener.h"
#include "state/StateListener.h"
@@ -87,6 +88,13 @@
NO_DATA = 9
};
+enum MetricType {
+ METRIC_TYPE_EVENT = 1,
+ METRIC_TYPE_COUNT = 2,
+ METRIC_TYPE_DURATION = 3,
+ METRIC_TYPE_GAUGE = 4,
+ METRIC_TYPE_VALUE = 5,
+};
struct Activation {
Activation(const ActivationType& activationType, const int64_t ttlNs)
: ttl_ns(ttlNs),
@@ -130,7 +138,7 @@
public:
MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
const int conditionIndex, const vector<ConditionState>& initialConditionCache,
- const sp<ConditionWizard>& wizard,
+ const sp<ConditionWizard>& wizard, const uint64_t protoHash,
const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
eventDeactivationMap,
@@ -144,6 +152,34 @@
return conditionIndex >= 0 ? initialConditionCache[conditionIndex] : ConditionState::kTrue;
}
+ // Update appropriate state on config updates. Primarily, all indices need to be updated.
+ // This metric and all of its dependencies are guaranteed to be preserved across the update.
+ // This function also updates several maps used by metricsManager.
+ // This function clears all anomaly trackers. All anomaly trackers need to be added again.
+ bool onConfigUpdated(
+ const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const sp<ConditionWizard>& wizard,
+ const std::unordered_map<int64_t, int>& metricToActivationMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return onConfigUpdatedLocked(config, configIndex, metricIndex, allAtomMatchingTrackers,
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap,
+ matcherWizard, allConditionTrackers, conditionTrackerMap,
+ wizard, metricToActivationMap, trackerToMetricMap,
+ conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation);
+ };
+
/**
* Force a partial bucket split on app upgrade
*/
@@ -202,6 +238,22 @@
dumpLatency, str_set, protoOutput);
}
+ virtual bool onConfigUpdatedLocked(
+ const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const sp<ConditionWizard>& wizard,
+ const std::unordered_map<int64_t, int>& metricToActivationMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation);
+
void clearPastBuckets(const int64_t dumpTimeNs) {
std::lock_guard<std::mutex> lock(mMutex);
return clearPastBucketsLocked(dumpTimeNs);
@@ -259,10 +311,16 @@
int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
// Start: getters/setters
- inline const int64_t& getMetricId() const {
+ inline int64_t getMetricId() const {
return mMetricId;
}
+ inline uint64_t getProtoHash() const {
+ return mProtoHash;
+ }
+
+ virtual MetricType getMetricType() const = 0;
+
// For test only.
inline int64_t getCurrentBucketNum() const {
return mCurrentBucketNum;
@@ -278,16 +336,24 @@
return mSlicedStateAtoms;
}
- /* If alert is valid, adds an AnomalyTracker and returns it. If invalid, returns nullptr. */
+ inline bool isValid() const {
+ return mValid;
+ }
+
+ /* Adds an AnomalyTracker and returns it. */
virtual sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
const sp<AlarmMonitor>& anomalyAlarmMonitor) {
std::lock_guard<std::mutex> lock(mMutex);
sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, mConfigKey);
- if (anomalyTracker != nullptr) {
- mAnomalyTrackers.push_back(anomalyTracker);
- }
+ mAnomalyTrackers.push_back(anomalyTracker);
return anomalyTracker;
}
+
+ /* Adds an AnomalyTracker that has already been created */
+ virtual void addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mAnomalyTrackers.push_back(anomalyTracker);
+ }
// End: getters/setters
protected:
/**
@@ -400,8 +466,14 @@
const int64_t mMetricId;
+ // Hash of the Metric's proto bytes from StatsdConfig, including any activations.
+ // Used to determine if the definition of this metric has changed across a config update.
+ const uint64_t mProtoHash;
+
const ConfigKey mConfigKey;
+ bool mValid;
+
// The time when this metric producer was first created. The end time for the current bucket
// can be computed from this based on mCurrentBucketNum.
int64_t mTimeBaseNs;
@@ -500,6 +572,14 @@
FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges);
FRIEND_TEST(MetricsManagerTest, TestInitialConditions);
+
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateMetricActivations);
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateCountMetrics);
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateEventMetrics);
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateGaugeMetrics);
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics);
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateMetricsMultipleTypes);
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateAlerts);
};
} // namespace statsd
diff --git a/bin/src/metrics/MetricsManager.cpp b/bin/src/metrics/MetricsManager.cpp
index 2d5aa56..f9b0a10 100644
--- a/bin/src/metrics/MetricsManager.cpp
+++ b/bin/src/metrics/MetricsManager.cpp
@@ -24,9 +24,10 @@
#include "condition/CombinationConditionTracker.h"
#include "condition/SimpleConditionTracker.h"
#include "guardrail/StatsdStats.h"
-#include "matchers/CombinationLogMatchingTracker.h"
-#include "matchers/SimpleLogMatchingTracker.h"
-#include "metrics_manager_util.h"
+#include "matchers/CombinationAtomMatchingTracker.h"
+#include "matchers/SimpleAtomMatchingTracker.h"
+#include "parsing_utils/config_update_utils.h"
+#include "parsing_utils/metrics_manager_util.h"
#include "state/StateManager.h"
#include "stats_log_util.h"
#include "stats_util.h"
@@ -77,22 +78,119 @@
refreshTtl(timeBaseNs);
mConfigValid = initStatsdConfig(
- key, config, *uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchers, mAllConditionTrackers,
- mAllMetricProducers, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers,
- mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap,
- mActivationAtomTrackerToMetricMap, mDeactivationAtomTrackerToMetricMap,
- mAlertTrackerMap, mMetricIndexesWithActivation, mNoReportMetricIds);
+ key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+ timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap,
+ mAllConditionTrackers, mConditionTrackerMap, mAllMetricProducers, mMetricProducerMap,
+ mAllAnomalyTrackers, mAllPeriodicAlarmTrackers, mConditionToMetricMap,
+ mTrackerToMetricMap, mTrackerToConditionMap, mActivationAtomTrackerToMetricMap,
+ mDeactivationAtomTrackerToMetricMap, mAlertTrackerMap, mMetricIndexesWithActivation,
+ mStateProtoHashes, mNoReportMetricIds);
mHashStringsInReport = config.hash_strings_in_metric_report();
mVersionStringsInReport = config.version_strings_in_metric_report();
mInstallerInReport = config.installer_in_metric_report();
+ createAllLogSourcesFromConfig(config);
+ mPullerManager->RegisterPullUidProvider(mConfigKey, this);
+
+ // Store the sub-configs used.
+ for (const auto& annotation : config.annotation()) {
+ mAnnotations.emplace_back(annotation.field_int64(), annotation.field_int32());
+ }
+ verifyGuardrailsAndUpdateStatsdStats();
+ initializeConfigActiveStatus();
+}
+
+MetricsManager::~MetricsManager() {
+ for (auto it : mAllMetricProducers) {
+ for (int atomId : it->getSlicedStateAtoms()) {
+ StateManager::getInstance().unregisterListener(atomId, it);
+ }
+ }
+ mPullerManager->UnregisterPullUidProvider(mConfigKey, this);
+
+ VLOG("~MetricsManager()");
+}
+
+bool MetricsManager::updateConfig(const StatsdConfig& config, const int64_t timeBaseNs,
+ const int64_t currentTimeNs,
+ const sp<AlarmMonitor>& anomalyAlarmMonitor,
+ const sp<AlarmMonitor>& periodicAlarmMonitor) {
+ vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers;
+ unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+ vector<sp<ConditionTracker>> newConditionTrackers;
+ unordered_map<int64_t, int> newConditionTrackerMap;
+ map<int64_t, uint64_t> newStateProtoHashes;
+ vector<sp<MetricProducer>> newMetricProducers;
+ unordered_map<int64_t, int> newMetricProducerMap;
+ vector<sp<AnomalyTracker>> newAnomalyTrackers;
+ unordered_map<int64_t, int> newAlertTrackerMap;
+ vector<sp<AlarmTracker>> newPeriodicAlarmTrackers;
+ mTagIds.clear();
+ mConditionToMetricMap.clear();
+ mTrackerToMetricMap.clear();
+ mTrackerToConditionMap.clear();
+ mActivationAtomTrackerToMetricMap.clear();
+ mDeactivationAtomTrackerToMetricMap.clear();
+ mMetricIndexesWithActivation.clear();
+ mNoReportMetricIds.clear();
+ mConfigValid = updateStatsdConfig(
+ mConfigKey, config, mUidMap, mPullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+ timeBaseNs, currentTimeNs, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap,
+ mAllConditionTrackers, mConditionTrackerMap, mAllMetricProducers, mMetricProducerMap,
+ mAllAnomalyTrackers, mAlertTrackerMap, mStateProtoHashes, mTagIds,
+ newAtomMatchingTrackers, newAtomMatchingTrackerMap, newConditionTrackers,
+ newConditionTrackerMap, newMetricProducers, newMetricProducerMap, newAnomalyTrackers,
+ newAlertTrackerMap, newPeriodicAlarmTrackers, mConditionToMetricMap,
+ mTrackerToMetricMap, mTrackerToConditionMap, mActivationAtomTrackerToMetricMap,
+ mDeactivationAtomTrackerToMetricMap, mMetricIndexesWithActivation, newStateProtoHashes,
+ mNoReportMetricIds);
+ mAllAtomMatchingTrackers = newAtomMatchingTrackers;
+ mAtomMatchingTrackerMap = newAtomMatchingTrackerMap;
+ mAllConditionTrackers = newConditionTrackers;
+ mConditionTrackerMap = newConditionTrackerMap;
+ mAllMetricProducers = newMetricProducers;
+ mMetricProducerMap = newMetricProducerMap;
+ mStateProtoHashes = newStateProtoHashes;
+ mAllAnomalyTrackers = newAnomalyTrackers;
+ mAlertTrackerMap = newAlertTrackerMap;
+ mAllPeriodicAlarmTrackers = newPeriodicAlarmTrackers;
+
+ mTtlNs = config.has_ttl_in_seconds() ? config.ttl_in_seconds() * NS_PER_SEC : -1;
+ refreshTtl(currentTimeNs);
+
+ mHashStringsInReport = config.hash_strings_in_metric_report();
+ mVersionStringsInReport = config.version_strings_in_metric_report();
+ mInstallerInReport = config.installer_in_metric_report();
+ mWhitelistedAtomIds.clear();
+ mWhitelistedAtomIds.insert(config.whitelisted_atom_ids().begin(),
+ config.whitelisted_atom_ids().end());
+ mShouldPersistHistory = config.persist_locally();
+
+ // Store the sub-configs used.
+ mAnnotations.clear();
+ for (const auto& annotation : config.annotation()) {
+ mAnnotations.emplace_back(annotation.field_int64(), annotation.field_int32());
+ }
+
+ mAllowedUid.clear();
+ mAllowedPkg.clear();
+ mDefaultPullUids.clear();
+ mPullAtomUids.clear();
+ mPullAtomPackages.clear();
+ createAllLogSourcesFromConfig(config);
+
+ verifyGuardrailsAndUpdateStatsdStats();
+ initializeConfigActiveStatus();
+ return mConfigValid;
+}
+
+void MetricsManager::createAllLogSourcesFromConfig(const StatsdConfig& config) {
// Init allowed pushed atom uids.
if (config.allowed_log_source_size() == 0) {
mConfigValid = false;
ALOGE("Log source allowlist is empty! This config won't get any data. Suggest adding at "
- "least AID_SYSTEM and AID_STATSD to the allowed_log_source field.");
+ "least AID_SYSTEM and AID_STATSD to the allowed_log_source field.");
} else {
for (const auto& source : config.allowed_log_source()) {
auto it = UidMap::sAidToUidMapping.find(source);
@@ -107,7 +205,7 @@
ALOGE("Too many log sources. This is likely to be an error in the config.");
mConfigValid = false;
} else {
- initLogSourceWhiteList();
+ initAllowedLogSources();
}
}
@@ -143,17 +241,13 @@
} else {
initPullAtomSources();
}
- mPullerManager->RegisterPullUidProvider(mConfigKey, this);
+}
- // Store the sub-configs used.
- for (const auto& annotation : config.annotation()) {
- mAnnotations.emplace_back(annotation.field_int64(), annotation.field_int32());
- }
-
+void MetricsManager::verifyGuardrailsAndUpdateStatsdStats() {
// Guardrail. Reject the config if it's too big.
if (mAllMetricProducers.size() > StatsdStats::kMaxMetricCountPerConfig ||
mAllConditionTrackers.size() > StatsdStats::kMaxConditionCountPerConfig ||
- mAllAtomMatchers.size() > StatsdStats::kMaxMatcherCountPerConfig) {
+ mAllAtomMatchingTrackers.size() > StatsdStats::kMaxMatcherCountPerConfig) {
ALOGE("This config is too big! Reject!");
mConfigValid = false;
}
@@ -161,41 +255,24 @@
ALOGE("This config has too many alerts! Reject!");
mConfigValid = false;
}
-
- mIsAlwaysActive = (mMetricIndexesWithActivation.size() != mAllMetricProducers.size()) ||
- (mAllMetricProducers.size() == 0);
- bool isActive = mIsAlwaysActive;
- for (int metric : mMetricIndexesWithActivation) {
- isActive |= mAllMetricProducers[metric]->isActive();
- }
- mIsActive = isActive;
- VLOG("mIsActive is initialized to %d", mIsActive)
-
// no matter whether this config is valid, log it in the stats.
StatsdStats::getInstance().noteConfigReceived(
- key, mAllMetricProducers.size(), mAllConditionTrackers.size(), mAllAtomMatchers.size(),
- mAllAnomalyTrackers.size(), mAnnotations, mConfigValid);
- // Check active
- for (const auto& metric : mAllMetricProducers) {
- if (metric->isActive()) {
- mIsActive = true;
- break;
- }
- }
+ mConfigKey, mAllMetricProducers.size(), mAllConditionTrackers.size(),
+ mAllAtomMatchingTrackers.size(), mAllAnomalyTrackers.size(), mAnnotations,
+ mConfigValid);
}
-MetricsManager::~MetricsManager() {
- for (auto it : mAllMetricProducers) {
- for (int atomId : it->getSlicedStateAtoms()) {
- StateManager::getInstance().unregisterListener(atomId, it);
- }
+void MetricsManager::initializeConfigActiveStatus() {
+ mIsAlwaysActive = (mMetricIndexesWithActivation.size() != mAllMetricProducers.size()) ||
+ (mAllMetricProducers.size() == 0);
+ mIsActive = mIsAlwaysActive;
+ for (int metric : mMetricIndexesWithActivation) {
+ mIsActive |= mAllMetricProducers[metric]->isActive();
}
- mPullerManager->UnregisterPullUidProvider(mConfigKey, this);
-
- VLOG("~MetricsManager()");
+ VLOG("mIsActive is initialized to %d", mIsActive);
}
-void MetricsManager::initLogSourceWhiteList() {
+void MetricsManager::initAllowedLogSources() {
std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
mAllowedLogSources.clear();
mAllowedLogSources.insert(mAllowedUid.begin(), mAllowedUid.end());
@@ -239,7 +316,7 @@
if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) {
// We will re-initialize the whole list because we don't want to keep the multi mapping of
// UID<->pkg inside MetricsManager to reduce the memory usage.
- initLogSourceWhiteList();
+ initAllowedLogSources();
}
for (const auto& it : mPullAtomPackages) {
@@ -260,7 +337,7 @@
if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) {
// We will re-initialize the whole list because we don't want to keep the multi mapping of
// UID<->pkg inside MetricsManager to reduce the memory usage.
- initLogSourceWhiteList();
+ initAllowedLogSources();
}
for (const auto& it : mPullAtomPackages) {
@@ -280,7 +357,7 @@
if (mAllowedPkg.size() == 0) {
return;
}
- initLogSourceWhiteList();
+ initAllowedLogSources();
}
void MetricsManager::onStatsdInitCompleted(const int64_t& eventTimeNs) {
@@ -484,11 +561,12 @@
return;
}
- vector<MatchingState> matcherCache(mAllAtomMatchers.size(), MatchingState::kNotComputed);
+ vector<MatchingState> matcherCache(mAllAtomMatchingTrackers.size(),
+ MatchingState::kNotComputed);
// Evaluate all atom matchers.
- for (auto& matcher : mAllAtomMatchers) {
- matcher->onLogEvent(event, mAllAtomMatchers, matcherCache);
+ for (auto& matcher : mAllAtomMatchingTrackers) {
+ matcher->onLogEvent(event, mAllAtomMatchingTrackers, matcherCache);
}
// Set of metrics that received an activation cancellation.
@@ -578,10 +656,10 @@
}
// For matched AtomMatchers, tell relevant metrics that a matched event has come.
- for (size_t i = 0; i < mAllAtomMatchers.size(); i++) {
+ for (size_t i = 0; i < mAllAtomMatchingTrackers.size(); i++) {
if (matcherCache[i] == MatchingState::kMatched) {
StatsdStats::getInstance().noteMatcherMatched(mConfigKey,
- mAllAtomMatchers[i]->getId());
+ mAllAtomMatchingTrackers[i]->getId());
auto pair = mTrackerToMetricMap.find(i);
if (pair != mTrackerToMetricMap.end()) {
auto& metricList = pair->second;
diff --git a/bin/src/metrics/MetricsManager.h b/bin/src/metrics/MetricsManager.h
index d17a1a8..a7b6165 100644
--- a/bin/src/metrics/MetricsManager.h
+++ b/bin/src/metrics/MetricsManager.h
@@ -25,7 +25,7 @@
#include "packages/modules/StatsD/bin/src/statsd_config.pb.h"
#include "packages/modules/StatsD/bin/src/statsd_metadata.pb.h"
#include "logd/LogEvent.h"
-#include "matchers/LogMatchingTracker.h"
+#include "matchers/AtomMatchingTracker.h"
#include "metrics/MetricProducer.h"
#include "packages/UidMap.h"
@@ -46,6 +46,10 @@
virtual ~MetricsManager();
+ bool updateConfig(const StatsdConfig& config, const int64_t timeBaseNs,
+ const int64_t currentTimeNs, const sp<AlarmMonitor>& anomalyAlarmMonitor,
+ const sp<AlarmMonitor>& periodicAlarmMonitor);
+
// Return whether the configuration is valid.
bool isConfigValid() const;
@@ -168,7 +172,7 @@
bool mVersionStringsInReport = false;
bool mInstallerInReport = false;
- const int64_t mTtlNs;
+ int64_t mTtlNs;
int64_t mTtlEndNs;
int64_t mLastReportTimeNs;
@@ -189,7 +193,7 @@
// To guard access to mAllowedLogSources
mutable std::mutex mAllowedLogSourcesMutex;
- const std::set<int32_t> mWhitelistedAtomIds;
+ std::set<int32_t> mWhitelistedAtomIds;
// We can pull any atom from these uids.
std::set<int32_t> mDefaultPullUids;
@@ -207,13 +211,12 @@
// Contains the annotations passed in with StatsdConfig.
std::list<std::pair<const int64_t, const int32_t>> mAnnotations;
- const bool mShouldPersistHistory;
-
+ bool mShouldPersistHistory;
// All event tags that are interesting to my metrics.
std::set<int> mTagIds;
- // We only store the sp of LogMatchingTracker, MetricProducer, and ConditionTracker in
+ // We only store the sp of AtomMatchingTracker, MetricProducer, and ConditionTracker in
// MetricsManager. There are relationships between them, and the relationships are denoted by
// index instead of pointers. The reasons for this are: (1) the relationship between them are
// complicated, so storing index instead of pointers reduces the risk that A holds B's sp, and B
@@ -221,7 +224,7 @@
// the related results from a cache using the index.
// Hold all the atom matchers from the config.
- std::vector<sp<LogMatchingTracker>> mAllAtomMatchers;
+ std::vector<sp<AtomMatchingTracker>> mAllAtomMatchingTrackers;
// Hold all the conditions from the config.
std::vector<sp<ConditionTracker>> mAllConditionTrackers;
@@ -235,23 +238,35 @@
// Hold all periodic alarm trackers.
std::vector<sp<AlarmTracker>> mAllPeriodicAlarmTrackers;
+ // To make updating configs faster, we map the id of a AtomMatchingTracker, MetricProducer, and
+ // ConditionTracker to its index in the corresponding vector.
+
+ // Maps the id of an atom matching tracker to its index in mAllAtomMatchingTrackers.
+ std::unordered_map<int64_t, int> mAtomMatchingTrackerMap;
+
+ // Maps the id of a condition tracker to its index in mAllConditionTrackers.
+ std::unordered_map<int64_t, int> mConditionTrackerMap;
+
+ // Maps the id of a metric producer to its index in mAllMetricProducers.
+ std::unordered_map<int64_t, int> mMetricProducerMap;
+
// To make the log processing more efficient, we want to do as much filtering as possible
// before we go into individual trackers and conditions to match.
// 1st filter: check if the event tag id is in mTagIds.
// 2nd filter: if it is, we parse the event because there is at least one member is interested.
- // then pass to all LogMatchingTrackers (itself also filter events by ids).
- // 3nd filter: for LogMatchingTrackers that matched this event, we pass this event to the
+ // then pass to all AtomMatchingTrackers (itself also filter events by ids).
+ // 3nd filter: for AtomMatchingTrackers that matched this event, we pass this event to the
// ConditionTrackers and MetricProducers that use this matcher.
// 4th filter: for ConditionTrackers that changed value due to this event, we pass
// new conditions to metrics that use this condition.
// The following map is initialized from the statsd_config.
- // Maps from the index of the LogMatchingTracker to index of MetricProducer.
+ // Maps from the index of the AtomMatchingTracker to index of MetricProducer.
std::unordered_map<int, std::vector<int>> mTrackerToMetricMap;
- // Maps from LogMatchingTracker to ConditionTracker
+ // Maps from AtomMatchingTracker to ConditionTracker
std::unordered_map<int, std::vector<int>> mTrackerToConditionMap;
// Maps from ConditionTracker to MetricProducer
@@ -269,10 +284,22 @@
std::vector<int> mMetricIndexesWithActivation;
- void initLogSourceWhiteList();
+ void initAllowedLogSources();
void initPullAtomSources();
+ // Only called on config creation/update to initialize log sources from the config.
+ // Calls initAllowedLogSources and initPullAtomSources. Sets mConfigValid to false on error.
+ void createAllLogSourcesFromConfig(const StatsdConfig& config);
+
+ // Verifies the config meets guardrails and updates statsdStats.
+ // Sets mConfigValid to false on error. Should be called on config creation/update
+ void verifyGuardrailsAndUpdateStatsdStats();
+
+ // Initializes mIsAlwaysActive and mIsActive.
+ // Should be called on config creation/update.
+ void initializeConfigActiveStatus();
+
// The metrics that don't need to be uploaded or even reported.
std::set<int64_t> mNoReportMetricIds;
@@ -282,6 +309,9 @@
// The config is always active if any metric in the config does not have an activation signal.
bool mIsAlwaysActive;
+ // Hashes of the States used in this config, keyed by the state id, used in config updates.
+ std::map<int64_t, uint64_t> mStateProtoHashes;
+
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions);
FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid);
@@ -304,6 +334,7 @@
FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms);
FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric);
+ FRIEND_TEST(ConfigUpdateE2eAbTest, TestConfigTtl);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations);
@@ -311,6 +342,7 @@
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
FRIEND_TEST(MetricsManagerTest, TestLogSources);
+ FRIEND_TEST(MetricsManagerTest, TestLogSourcesOnConfigUpdate);
FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot);
diff --git a/bin/src/metrics/ValueMetricProducer.cpp b/bin/src/metrics/ValueMetricProducer.cpp
index 5987a72..22fdf16 100644
--- a/bin/src/metrics/ValueMetricProducer.cpp
+++ b/bin/src/metrics/ValueMetricProducer.cpp
@@ -18,12 +18,14 @@
#include "Log.h"
#include "ValueMetricProducer.h"
-#include "../guardrail/StatsdStats.h"
-#include "../stats_log_util.h"
#include <limits.h>
#include <stdlib.h>
+#include "../guardrail/StatsdStats.h"
+#include "../stats_log_util.h"
+#include "metrics/parsing_utils/metrics_manager_util.h"
+
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_DOUBLE;
@@ -79,16 +81,17 @@
ValueMetricProducer::ValueMetricProducer(
const ConfigKey& key, const ValueMetric& metric, const int conditionIndex,
const vector<ConditionState>& initialConditionCache,
- const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
- const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int64_t timeBaseNs,
- const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const sp<ConditionWizard>& conditionWizard, const uint64_t protoHash,
+ const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard,
+ const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs,
+ const sp<StatsPullerManager>& pullerManager,
const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
const vector<int>& slicedStateAtoms,
const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
: MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache,
- conditionWizard, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
- stateGroupMap),
+ conditionWizard, protoHash, eventActivationMap, eventDeactivationMap,
+ slicedStateAtoms, stateGroupMap),
mWhatMatcherIndex(whatMatcherIndex),
mEventMatcherWizard(matcherWizard),
mPullerManager(pullerManager),
@@ -183,6 +186,48 @@
}
}
+bool ValueMetricProducer::onConfigUpdatedLocked(
+ const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
+ if (!MetricProducer::onConfigUpdatedLocked(
+ config, configIndex, metricIndex, allAtomMatchingTrackers,
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+ allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap,
+ trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+ return false;
+ }
+
+ const ValueMetric& metric = config.value_metric(configIndex);
+ // Update appropriate indices: mWhatMatcherIndex, mConditionIndex and MetricsManager maps.
+ if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, /*enforceOneAtom=*/false,
+ allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+ trackerToMetricMap, mWhatMatcherIndex)) {
+ return false;
+ }
+
+ if (metric.has_condition() &&
+ !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+ metric.links(), allConditionTrackers, mConditionTrackerIndex,
+ conditionToMetricMap)) {
+ return false;
+ }
+ sp<EventMatcherWizard> tmpEventWizard = mEventMatcherWizard;
+ mEventMatcherWizard = matcherWizard;
+ return true;
+}
+
void ValueMetricProducer::onStateChanged(int64_t eventTimeNs, int32_t atomId,
const HashableDimensionKey& primaryKey,
const FieldValue& oldState, const FieldValue& newState) {
@@ -300,7 +345,6 @@
protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_BUCKET_DROP_REASON, dropEvent.reason);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DROP_TIME,
(long long)(NanoToMillis(dropEvent.dropTimeNs)));
- ;
protoOutput->end(dropEventToken);
}
protoOutput->end(wrapperToken);
@@ -345,8 +389,11 @@
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
(long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
}
- // only write the condition timer value if the metric has a condition.
- if (mConditionTrackerIndex >= 0) {
+ // We only write the condition timer value if the metric has a
+ // condition and/or is sliced by state.
+ // If the metric is sliced by state, the condition timer value is
+ // also sliced by state to reflect time spent in that state.
+ if (mConditionTrackerIndex >= 0 || !mSlicedStateAtoms.empty()) {
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_TRUE_NS,
(long long)bucket.mConditionTrueNs);
}
@@ -411,7 +458,7 @@
void ValueMetricProducer::resetBase() {
for (auto& slice : mCurrentBaseInfo) {
- for (auto& baseInfo : slice.second) {
+ for (auto& baseInfo : slice.second.baseInfos) {
baseInfo.hasBase = false;
}
}
@@ -453,6 +500,8 @@
// Let condition timer know of new active state.
mConditionTimer.onConditionChanged(mIsActive, eventTimeNs);
+
+ updateCurrentSlicedBucketConditionTimers(mIsActive, eventTimeNs);
}
void ValueMetricProducer::onConditionChangedLocked(const bool condition,
@@ -475,6 +524,8 @@
invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET);
mCondition = ConditionState::kUnknown;
mConditionTimer.onConditionChanged(mCondition, eventTimeNs);
+
+ updateCurrentSlicedBucketConditionTimers(mCondition, eventTimeNs);
return;
}
@@ -516,6 +567,29 @@
flushIfNeededLocked(eventTimeNs);
mConditionTimer.onConditionChanged(mCondition, eventTimeNs);
+
+ updateCurrentSlicedBucketConditionTimers(mCondition, eventTimeNs);
+}
+
+void ValueMetricProducer::updateCurrentSlicedBucketConditionTimers(bool newCondition,
+ int64_t eventTimeNs) {
+ if (mSlicedStateAtoms.empty()) {
+ return;
+ }
+
+ // Utilize the current state key of each DimensionsInWhat key to determine
+ // which condition timers to update.
+ //
+ // Assumes that the MetricDimensionKey exists in `mCurrentSlicedBucket`.
+ bool inPulledData;
+ for (const auto& [dimensionInWhatKey, dimensionInWhatInfo] : mCurrentBaseInfo) {
+ // If the new condition is true, turn ON the condition timer only if
+ // the DimensionInWhat key was present in the pulled data.
+ inPulledData = dimensionInWhatInfo.hasCurrentState;
+ mCurrentSlicedBucket[MetricDimensionKey(dimensionInWhatKey,
+ dimensionInWhatInfo.currentState)]
+ .conditionTimer.onConditionChanged(newCondition && inPulledData, eventTimeNs);
+ }
}
void ValueMetricProducer::prepareFirstBucketLocked() {
@@ -617,15 +691,21 @@
// 2. A superset of the current mStateChangePrimaryKey
// was not found in the new pulled data (i.e. not in mMatchedDimensionInWhatKeys)
// then we need to reset the base.
- for (auto& slice : mCurrentSlicedBucket) {
- const auto& whatKey = slice.first.getDimensionKeyInWhat();
+ for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) {
+ const auto& whatKey = metricDimensionKey.getDimensionKeyInWhat();
bool presentInPulledData =
mMatchedMetricDimensionKeys.find(whatKey) != mMatchedMetricDimensionKeys.end();
if (!presentInPulledData && whatKey.contains(mStateChangePrimaryKey.second)) {
auto it = mCurrentBaseInfo.find(whatKey);
- for (auto& baseInfo : it->second) {
+ for (auto& baseInfo : it->second.baseInfos) {
baseInfo.hasBase = false;
}
+ // Set to false when DimensionInWhat key is not present in a pull.
+ // Used in onMatchedLogEventInternalLocked() to ensure the condition
+ // timer is turned on the next pull when data is present.
+ it->second.hasCurrentState = false;
+ // Turn OFF condition timer for keys not present in pulled data.
+ currentValueBucket.conditionTimer.onConditionChanged(false, eventElapsedTimeNs);
}
}
mMatchedMetricDimensionKeys.clear();
@@ -652,7 +732,7 @@
(unsigned long)mCurrentSlicedBucket.size());
if (verbose) {
for (const auto& it : mCurrentSlicedBucket) {
- for (const auto& interval : it.second) {
+ for (const auto& interval : it.second.intervals) {
fprintf(out, "\t(what)%s\t(states)%s (value)%s\n",
it.first.getDimensionKeyInWhat().toString().c_str(),
it.first.getStateValuesKey().toString().c_str(),
@@ -733,6 +813,11 @@
return false;
}
+bool ValueMetricProducer::multipleBucketsSkipped(const int64_t numBucketsForward) {
+ // Skip buckets if this is a pulled metric or a pushed metric that is diffed.
+ return numBucketsForward > 1 && (mIsPulled || mUseDiff);
+}
+
void ValueMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition, const LogEvent& event,
@@ -783,23 +868,28 @@
return;
}
- vector<BaseInfo>& baseInfos = mCurrentBaseInfo[whatKey];
+ const auto& returnVal =
+ mCurrentBaseInfo.emplace(whatKey, DimensionsInWhatInfo(getUnknownStateKey()));
+ DimensionsInWhatInfo& dimensionsInWhatInfo = returnVal.first->second;
+ const HashableDimensionKey oldStateKey = dimensionsInWhatInfo.currentState;
+ vector<BaseInfo>& baseInfos = dimensionsInWhatInfo.baseInfos;
if (baseInfos.size() < mFieldMatchers.size()) {
VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size());
baseInfos.resize(mFieldMatchers.size());
}
- for (BaseInfo& baseInfo : baseInfos) {
- if (!baseInfo.hasCurrentState) {
- baseInfo.currentState = getUnknownStateKey();
- baseInfo.hasCurrentState = true;
- }
+ // Ensure we turn on the condition timer in the case where dimensions
+ // were missing on a previous pull due to a state change.
+ bool stateChange = oldStateKey != stateKey;
+ if (!dimensionsInWhatInfo.hasCurrentState) {
+ stateChange = true;
+ dimensionsInWhatInfo.hasCurrentState = true;
}
// We need to get the intervals stored with the previous state key so we can
// close these value intervals.
- const auto oldStateKey = baseInfos[0].currentState;
- vector<Interval>& intervals = mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)];
+ vector<Interval>& intervals =
+ mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)].intervals;
if (intervals.size() < mFieldMatchers.size()) {
VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size());
intervals.resize(mFieldMatchers.size());
@@ -813,14 +903,14 @@
// Discussion here: http://ag/6124370.
bool useAnomalyDetection = true;
+ dimensionsInWhatInfo.hasCurrentState = true;
+ dimensionsInWhatInfo.currentState = stateKey;
for (int i = 0; i < (int)mFieldMatchers.size(); i++) {
const Matcher& matcher = mFieldMatchers[i];
BaseInfo& baseInfo = baseInfos[i];
Interval& interval = intervals[i];
interval.valueIndex = i;
Value value;
- baseInfo.hasCurrentState = true;
- baseInfo.currentState = stateKey;
if (!getDoubleOrLong(event, matcher, value)) {
VLOG("Failed to get value %d from event %s", i, event.ToString().c_str());
StatsdStats::getInstance().noteBadValueType(mMetricId);
@@ -910,8 +1000,20 @@
interval.sampleSize += 1;
}
- // Only trigger the tracker if all intervals are correct
- if (useAnomalyDetection) {
+ // State change.
+ if (!mSlicedStateAtoms.empty() && stateChange) {
+ // Turn OFF the condition timer for the previous state key.
+ mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)]
+ .conditionTimer.onConditionChanged(false, eventTimeNs);
+
+ // Turn ON the condition timer for the new state key.
+ mCurrentSlicedBucket[MetricDimensionKey(whatKey, stateKey)]
+ .conditionTimer.onConditionChanged(true, eventTimeNs);
+ }
+
+ // Only trigger the tracker if all intervals are correct and we have not skipped the bucket due
+ // to MULTIPLE_BUCKETS_SKIPPED.
+ if (useAnomalyDetection && !multipleBucketsSkipped(calcBucketsForwardCount(eventTimeNs))) {
// TODO: propgate proper values down stream when anomaly support doubles
long wholeBucketVal = intervals[0].value.long_value;
auto prev = mCurrentFullBucket.find(eventKey);
@@ -961,9 +1063,7 @@
int64_t bucketEndTime = fullBucketEndTimeNs;
int64_t numBucketsForward = calcBucketsForwardCount(eventTimeNs);
- // Skip buckets if this is a pulled metric or a pushed metric that is diffed.
- if (numBucketsForward > 1 && (mIsPulled || mUseDiff)) {
-
+ if (multipleBucketsSkipped(numBucketsForward)) {
VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
StatsdStats::getInstance().noteSkippedForwardBuckets(mMetricId);
// Something went wrong. Maybe the device was sleeping for a long time. It is better
@@ -985,12 +1085,18 @@
if (!mCurrentBucketIsSkipped) {
bool bucketHasData = false;
// The current bucket is large enough to keep.
- for (const auto& slice : mCurrentSlicedBucket) {
- ValueBucket bucket = buildPartialBucket(bucketEndTime, slice.second);
- bucket.mConditionTrueNs = conditionTrueDuration;
+ for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) {
+ PastValueBucket bucket =
+ buildPartialBucket(bucketEndTime, currentValueBucket.intervals);
+ if (!mSlicedStateAtoms.empty()) {
+ bucket.mConditionTrueNs =
+ currentValueBucket.conditionTimer.newBucketStart(bucketEndTime);
+ } else {
+ bucket.mConditionTrueNs = conditionTrueDuration;
+ }
// it will auto create new vector of ValuebucketInfo if the key is not found.
if (bucket.valueIndex.size() > 0) {
- auto& bucketList = mPastBuckets[slice.first];
+ auto& bucketList = mPastBuckets[metricDimensionKey];
bucketList.push_back(bucket);
bucketHasData = true;
}
@@ -1018,17 +1124,24 @@
buildDropEvent(eventTimeNs, BucketDropReason::NO_DATA));
mSkippedBuckets.emplace_back(bucketInGap);
}
-
appendToFullBucket(eventTimeNs > fullBucketEndTimeNs);
initCurrentSlicedBucket(nextBucketStartTimeNs);
// Update the condition timer again, in case we skipped buckets.
mConditionTimer.newBucketStart(nextBucketStartTimeNs);
+
+ // NOTE: Update the condition timers in `mCurrentSlicedBucket` only when slicing
+ // by state. Otherwise, the "global" condition timer will be used.
+ if (!mSlicedStateAtoms.empty()) {
+ for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) {
+ currentValueBucket.conditionTimer.newBucketStart(nextBucketStartTimeNs);
+ }
+ }
mCurrentBucketNum += numBucketsForward;
}
-ValueBucket ValueMetricProducer::buildPartialBucket(int64_t bucketEndTime,
- const std::vector<Interval>& intervals) {
- ValueBucket bucket;
+PastValueBucket ValueMetricProducer::buildPartialBucket(int64_t bucketEndTime,
+ const std::vector<Interval>& intervals) {
+ PastValueBucket bucket;
bucket.mBucketStartNs = mCurrentBucketStartTimeNs;
bucket.mBucketEndNs = bucketEndTime;
for (const auto& interval : intervals) {
@@ -1055,7 +1168,7 @@
// Cleanup data structure to aggregate values.
for (auto it = mCurrentSlicedBucket.begin(); it != mCurrentSlicedBucket.end();) {
bool obsolete = true;
- for (auto& interval : it->second) {
+ for (auto& interval : it->second.intervals) {
interval.hasValue = false;
interval.sampleSize = 0;
if (interval.seenNewData) {
@@ -1064,6 +1177,17 @@
interval.seenNewData = false;
}
+ if (obsolete && !mSlicedStateAtoms.empty()) {
+ // When slicing by state, only delete the MetricDimensionKey when the
+ // state key in the MetricDimensionKey is not the current state key.
+ const HashableDimensionKey& dimensionInWhatKey = it->first.getDimensionKeyInWhat();
+ const auto& currentBaseInfoItr = mCurrentBaseInfo.find(dimensionInWhatKey);
+
+ if ((currentBaseInfoItr != mCurrentBaseInfo.end()) &&
+ (it->first.getStateValuesKey() == currentBaseInfoItr->second.currentState)) {
+ obsolete = false;
+ }
+ }
if (obsolete) {
it = mCurrentSlicedBucket.erase(it);
} else {
@@ -1099,11 +1223,11 @@
// Accumulate partial buckets with current value and then send to anomaly tracker.
if (mCurrentFullBucket.size() > 0) {
for (const auto& slice : mCurrentSlicedBucket) {
- if (hitFullBucketGuardRailLocked(slice.first)) {
+ if (hitFullBucketGuardRailLocked(slice.first) || slice.second.intervals.empty()) {
continue;
}
// TODO: fix this when anomaly can accept double values
- auto& interval = slice.second[0];
+ auto& interval = slice.second.intervals[0];
if (interval.hasValue) {
mCurrentFullBucket[slice.first] += interval.value.long_value;
}
@@ -1120,9 +1244,9 @@
// Skip aggregating the partial buckets since there's no previous partial bucket.
for (const auto& slice : mCurrentSlicedBucket) {
for (auto& tracker : mAnomalyTrackers) {
- if (tracker != nullptr) {
+ if (tracker != nullptr && !slice.second.intervals.empty()) {
// TODO: fix this when anomaly can accept double values
- auto& interval = slice.second[0];
+ auto& interval = slice.second.intervals[0];
if (interval.hasValue) {
tracker->addPastBucket(slice.first, interval.value.long_value,
mCurrentBucketNum);
@@ -1134,10 +1258,12 @@
} else {
// Accumulate partial bucket.
for (const auto& slice : mCurrentSlicedBucket) {
- // TODO: fix this when anomaly can accept double values
- auto& interval = slice.second[0];
- if (interval.hasValue) {
- mCurrentFullBucket[slice.first] += interval.value.long_value;
+ if (!slice.second.intervals.empty()) {
+ // TODO: fix this when anomaly can accept double values
+ auto& interval = slice.second.intervals[0];
+ if (interval.hasValue) {
+ mCurrentFullBucket[slice.first] += interval.value.long_value;
+ }
}
}
}
diff --git a/bin/src/metrics/ValueMetricProducer.h b/bin/src/metrics/ValueMetricProducer.h
index a1eb702..1de0524 100644
--- a/bin/src/metrics/ValueMetricProducer.h
+++ b/bin/src/metrics/ValueMetricProducer.h
@@ -31,7 +31,7 @@
namespace os {
namespace statsd {
-struct ValueBucket {
+struct PastValueBucket {
int64_t mBucketStartNs;
int64_t mBucketEndNs;
std::vector<int> valueIndex;
@@ -41,21 +41,20 @@
int64_t mConditionTrueNs;
};
-
// Aggregates values within buckets.
//
// There are different events that might complete a bucket
// - a condition change
// - an app upgrade
// - an alarm set to the end of the bucket
-class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
+class ValueMetricProducer : public MetricProducer, public virtual PullDataReceiver {
public:
ValueMetricProducer(
const ConfigKey& key, const ValueMetric& valueMetric, const int conditionIndex,
const vector<ConditionState>& initialConditionCache,
- const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
- const sp<EventMatcherWizard>& matcherWizard, const int pullTagId,
- const int64_t timeBaseNs, const int64_t startTimeNs,
+ const sp<ConditionWizard>& conditionWizard, const uint64_t protoHash,
+ const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard,
+ const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs,
const sp<StatsPullerManager>& pullerManager,
const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
@@ -93,6 +92,10 @@
void onStateChanged(int64_t eventTimeNs, int32_t atomId, const HashableDimensionKey& primaryKey,
const FieldValue& oldState, const FieldValue& newState) override;
+ MetricType getMetricType() const override {
+ return METRIC_TYPE_VALUE;
+ }
+
protected:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
@@ -152,7 +155,23 @@
// causes the bucket to be invalidated will not notify StatsdStats.
void skipCurrentBucket(const int64_t dropTimeNs, const BucketDropReason reason);
- const int mWhatMatcherIndex;
+ bool onConfigUpdatedLocked(
+ const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const sp<ConditionWizard>& wizard,
+ const std::unordered_map<int64_t, int>& metricToActivationMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation) override;
+
+ int mWhatMatcherIndex;
sp<EventMatcherWizard> mEventMatcherWizard;
@@ -173,7 +192,7 @@
// if this is pulled metric
const bool mIsPulled;
- // internal state of an ongoing aggregation bucket.
+ // Tracks the value information of one value field.
typedef struct {
// Index in multi value aggregation.
int valueIndex;
@@ -188,25 +207,49 @@
bool seenNewData = false;
} Interval;
+ // Internal state of an ongoing aggregation bucket.
+ typedef struct CurrentValueBucket {
+ // If the `MetricDimensionKey` state key is the current state key, then
+ // the condition timer will be updated later (e.g. condition/state/active
+ // state change) with the correct condition and time.
+ CurrentValueBucket() : intervals(), conditionTimer(ConditionTimer(false, 0)) {}
+ // Value information for each value field of the metric.
+ std::vector<Interval> intervals;
+ // Tracks how long the condition is true.
+ ConditionTimer conditionTimer;
+ } CurrentValueBucket;
+
+ // Holds base information for diffing values from one value field.
typedef struct {
// Holds current base value of the dimension. Take diff and update if necessary.
Value base;
// Whether there is a base to diff to.
bool hasBase;
+ } BaseInfo;
+
+ // State key and base information for a specific DimensionsInWhat key.
+ typedef struct DimensionsInWhatInfo {
+ DimensionsInWhatInfo(const HashableDimensionKey& stateKey)
+ : baseInfos(), currentState(stateKey), hasCurrentState(false) {
+ }
+ std::vector<BaseInfo> baseInfos;
// Last seen state value(s).
HashableDimensionKey currentState;
// Whether this dimensions in what key has a current state key.
bool hasCurrentState;
- } BaseInfo;
+ } DimensionsInWhatInfo;
- std::unordered_map<MetricDimensionKey, std::vector<Interval>> mCurrentSlicedBucket;
+ // Tracks the internal state in the ongoing aggregation bucket for each DimensionsInWhat
+ // key and StateValuesKey pair.
+ std::unordered_map<MetricDimensionKey, CurrentValueBucket> mCurrentSlicedBucket;
- std::unordered_map<HashableDimensionKey, std::vector<BaseInfo>> mCurrentBaseInfo;
+ // Tracks current state key and base information for each DimensionsInWhat key.
+ std::unordered_map<HashableDimensionKey, DimensionsInWhatInfo> mCurrentBaseInfo;
std::unordered_map<MetricDimensionKey, int64_t> mCurrentFullBucket;
// Save the past buckets and we can clear when the StatsLogReport is dumped.
- std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>> mPastBuckets;
+ std::unordered_map<MetricDimensionKey, std::vector<PastValueBucket>> mPastBuckets;
const int64_t mMinBucketSizeNs;
@@ -219,11 +262,13 @@
void pullAndMatchEventsLocked(const int64_t timestampNs);
+ bool multipleBucketsSkipped(const int64_t numBucketsForward);
+
void accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData,
int64_t originalPullTimeNs, int64_t eventElapsedTimeNs);
- ValueBucket buildPartialBucket(int64_t bucketEndTime,
- const std::vector<Interval>& intervals);
+ PastValueBucket buildPartialBucket(int64_t bucketEndTime,
+ const std::vector<Interval>& intervals);
void initCurrentSlicedBucket(int64_t nextBucketStartTimeNs);
@@ -232,7 +277,11 @@
// Reset diff base and mHasGlobalBase
void resetBase();
- static const size_t kBucketSize = sizeof(ValueBucket{});
+ // Updates the condition timers in the current sliced bucket when there is a
+ // condition change or an active state change.
+ void updateCurrentSlicedBucketConditionTimers(bool newCondition, int64_t eventTimeNs);
+
+ static const size_t kBucketSize = sizeof(PastValueBucket{});
const size_t mDimensionSoftLimit;
@@ -317,6 +366,11 @@
FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey);
FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase);
FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMultipleDimensions);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataInStateChange);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithDataMissingInConditionChange);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataThenFlushBucket);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithNoPullOnBucketBoundary);
FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed);
FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed);
@@ -332,6 +386,8 @@
FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPulledValue);
FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPulledValueWhileConditionFalse);
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateValueMetrics);
+
friend class ValueMetricProducerTestHelper;
};
diff --git a/bin/src/metrics/duration_helper/DurationTracker.h b/bin/src/metrics/duration_helper/DurationTracker.h
index 8d59d13..cf1f437 100644
--- a/bin/src/metrics/duration_helper/DurationTracker.h
+++ b/bin/src/metrics/duration_helper/DurationTracker.h
@@ -71,7 +71,7 @@
sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
int64_t currentBucketStartNs, int64_t currentBucketNum, int64_t startTimeNs,
int64_t bucketSizeNs, bool conditionSliced, bool fullLink,
- const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
+ const std::vector<sp<AnomalyTracker>>& anomalyTrackers)
: mConfigKey(key),
mTrackerId(id),
mEventKey(eventKey),
@@ -89,6 +89,13 @@
virtual ~DurationTracker(){};
+ void onConfigUpdated(const sp<ConditionWizard>& wizard, const int conditionTrackerIndex) {
+ sp<ConditionWizard> tmpWizard = mWizard;
+ mWizard = wizard;
+ mConditionTrackerIndex = conditionTrackerIndex;
+ mAnomalyTrackers.clear();
+ };
+
virtual void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
const ConditionKey& conditionKey) = 0;
virtual void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
@@ -114,7 +121,7 @@
std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) = 0;
// Predict the anomaly timestamp given the current status.
- virtual int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
+ virtual int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
const int64_t currentTimestamp) const = 0;
// Dump internal states for debugging
virtual void dumpStates(FILE* out, bool verbose) const = 0;
@@ -126,6 +133,10 @@
// Replace old value with new value for the given state atom.
virtual void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState) = 0;
+ void addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker) {
+ mAnomalyTrackers.push_back(anomalyTracker);
+ }
+
protected:
int64_t getCurrentBucketEndTimeNs() const {
return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
@@ -191,7 +202,7 @@
sp<ConditionWizard> mWizard;
- const int mConditionTrackerIndex;
+ int mConditionTrackerIndex;
const int64_t mBucketSizeNs;
@@ -212,11 +223,14 @@
bool mHasLinksToAllConditionDimensionsInTracker;
- std::vector<sp<DurationAnomalyTracker>> mAnomalyTrackers;
+ std::vector<sp<AnomalyTracker>> mAnomalyTrackers;
FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);
FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm);
FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm);
+
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics);
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateAlerts);
};
} // namespace statsd
diff --git a/bin/src/metrics/duration_helper/MaxDurationTracker.cpp b/bin/src/metrics/duration_helper/MaxDurationTracker.cpp
index ee4e167..62f4982 100644
--- a/bin/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/bin/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -30,7 +30,7 @@
int64_t currentBucketStartNs, int64_t currentBucketNum,
int64_t startTimeNs, int64_t bucketSizeNs,
bool conditionSliced, bool fullLink,
- const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
+ const vector<sp<AnomalyTracker>>& anomalyTrackers)
: DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
currentBucketNum, startTimeNs, bucketSizeNs, conditionSliced, fullLink,
anomalyTrackers) {
@@ -288,7 +288,7 @@
// Note that we don't update mDuration here since it's only updated during noteStop.
}
-int64_t MaxDurationTracker::predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
+int64_t MaxDurationTracker::predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
const int64_t currentTimestamp) const {
// The allowed time we can continue in the current state is the
// (anomaly threshold) - max(elapsed time of the started mInfos).
diff --git a/bin/src/metrics/duration_helper/MaxDurationTracker.h b/bin/src/metrics/duration_helper/MaxDurationTracker.h
index 2891c6e..be2707c 100644
--- a/bin/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/bin/src/metrics/duration_helper/MaxDurationTracker.h
@@ -29,12 +29,10 @@
class MaxDurationTracker : public DurationTracker {
public:
MaxDurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
- sp<ConditionWizard> wizard, int conditionIndex,
- bool nesting,
- int64_t currentBucketStartNs, int64_t currentBucketNum,
- int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced,
- bool fullLink,
- const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers);
+ sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
+ int64_t currentBucketStartNs, int64_t currentBucketNum, int64_t startTimeNs,
+ int64_t bucketSizeNs, bool conditionSliced, bool fullLink,
+ const std::vector<sp<AnomalyTracker>>& anomalyTrackers);
MaxDurationTracker(const MaxDurationTracker& tracker) = default;
@@ -57,7 +55,7 @@
void onStateChanged(const int64_t timestamp, const int32_t atomId,
const FieldValue& newState) override;
- int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
+ int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
const int64_t currentTimestamp) const override;
void dumpStates(FILE* out, bool verbose) const override;
diff --git a/bin/src/metrics/duration_helper/OringDurationTracker.cpp b/bin/src/metrics/duration_helper/OringDurationTracker.cpp
index 0d49bbc..247e2e0 100644
--- a/bin/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/bin/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -28,7 +28,7 @@
const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
sp<ConditionWizard> wizard, int conditionIndex, bool nesting, int64_t currentBucketStartNs,
int64_t currentBucketNum, int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced,
- bool fullLink, const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
+ bool fullLink, const vector<sp<AnomalyTracker>>& anomalyTrackers)
: DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
currentBucketNum, startTimeNs, bucketSizeNs, conditionSliced, fullLink,
anomalyTrackers),
@@ -344,9 +344,8 @@
updateCurrentStateKey(atomId, newState);
}
-int64_t OringDurationTracker::predictAnomalyTimestampNs(
- const DurationAnomalyTracker& anomalyTracker, const int64_t eventTimestampNs) const {
-
+int64_t OringDurationTracker::predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
+ const int64_t eventTimestampNs) const {
// The anomaly threshold.
const int64_t thresholdNs = anomalyTracker.getAnomalyThreshold();
diff --git a/bin/src/metrics/duration_helper/OringDurationTracker.h b/bin/src/metrics/duration_helper/OringDurationTracker.h
index bd8017a..6eddee7 100644
--- a/bin/src/metrics/duration_helper/OringDurationTracker.h
+++ b/bin/src/metrics/duration_helper/OringDurationTracker.h
@@ -31,7 +31,7 @@
int conditionIndex, bool nesting, int64_t currentBucketStartNs,
int64_t currentBucketNum, int64_t startTimeNs, int64_t bucketSizeNs,
bool conditionSliced, bool fullLink,
- const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers);
+ const std::vector<sp<AnomalyTracker>>& anomalyTrackers);
OringDurationTracker(const OringDurationTracker& tracker) = default;
@@ -54,7 +54,7 @@
int64_t timestampNs,
std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
- int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
+ int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
const int64_t currentTimestamp) const override;
void dumpStates(FILE* out, bool verbose) const override;
diff --git a/bin/src/metrics/metrics_manager_util.cpp b/bin/src/metrics/metrics_manager_util.cpp
deleted file mode 100644
index 8917c36..0000000
--- a/bin/src/metrics/metrics_manager_util.cpp
+++ /dev/null
@@ -1,983 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "metrics_manager_util.h"
-
-#include <inttypes.h>
-
-#include "FieldValue.h"
-#include "MetricProducer.h"
-#include "condition/CombinationConditionTracker.h"
-#include "condition/SimpleConditionTracker.h"
-#include "external/StatsPullerManager.h"
-#include "matchers/CombinationLogMatchingTracker.h"
-#include "matchers/EventMatcherWizard.h"
-#include "matchers/SimpleLogMatchingTracker.h"
-#include "metrics/CountMetricProducer.h"
-#include "metrics/DurationMetricProducer.h"
-#include "metrics/EventMetricProducer.h"
-#include "metrics/GaugeMetricProducer.h"
-#include "metrics/ValueMetricProducer.h"
-#include "state/StateManager.h"
-#include "stats_util.h"
-
-using std::set;
-using std::unordered_map;
-using std::vector;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-namespace {
-
-bool hasLeafNode(const FieldMatcher& matcher) {
- if (!matcher.has_field()) {
- return false;
- }
- for (int i = 0; i < matcher.child_size(); ++i) {
- if (hasLeafNode(matcher.child(i))) {
- return true;
- }
- }
- return true;
-}
-
-} // namespace
-
-bool handleMetricWithLogTrackers(const int64_t what, const int metricIndex,
- const bool usedForDimension,
- const vector<sp<LogMatchingTracker>>& allAtomMatchers,
- const unordered_map<int64_t, int>& logTrackerMap,
- unordered_map<int, std::vector<int>>& trackerToMetricMap,
- int& logTrackerIndex) {
- auto logTrackerIt = logTrackerMap.find(what);
- if (logTrackerIt == logTrackerMap.end()) {
- ALOGW("cannot find the AtomMatcher \"%lld\" in config", (long long)what);
- return false;
- }
- if (usedForDimension && allAtomMatchers[logTrackerIt->second]->getAtomIds().size() > 1) {
- ALOGE("AtomMatcher \"%lld\" has more than one tag ids. When a metric has dimension, "
- "the \"what\" can only about one atom type.",
- (long long)what);
- return false;
- }
- logTrackerIndex = logTrackerIt->second;
- auto& metric_list = trackerToMetricMap[logTrackerIndex];
- metric_list.push_back(metricIndex);
- return true;
-}
-
-bool handlePullMetricTriggerWithLogTrackers(
- const int64_t trigger, const int metricIndex,
- const vector<sp<LogMatchingTracker>>& allAtomMatchers,
- const unordered_map<int64_t, int>& logTrackerMap,
- unordered_map<int, std::vector<int>>& trackerToMetricMap, int& logTrackerIndex) {
- auto logTrackerIt = logTrackerMap.find(trigger);
- if (logTrackerIt == logTrackerMap.end()) {
- ALOGW("cannot find the AtomMatcher \"%lld\" in config", (long long)trigger);
- return false;
- }
- if (allAtomMatchers[logTrackerIt->second]->getAtomIds().size() > 1) {
- ALOGE("AtomMatcher \"%lld\" has more than one tag ids."
- "Trigger can only be one atom type.",
- (long long)trigger);
- return false;
- }
- logTrackerIndex = logTrackerIt->second;
- auto& metric_list = trackerToMetricMap[logTrackerIndex];
- metric_list.push_back(metricIndex);
- return true;
-}
-
-bool handleMetricWithConditions(
- const int64_t condition, const int metricIndex,
- const unordered_map<int64_t, int>& conditionTrackerMap,
- const ::google::protobuf::RepeatedPtrField<::android::os::statsd::MetricConditionLink>&
- links,
- vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex,
- unordered_map<int, std::vector<int>>& conditionToMetricMap) {
- auto condition_it = conditionTrackerMap.find(condition);
- if (condition_it == conditionTrackerMap.end()) {
- ALOGW("cannot find Predicate \"%lld\" in the config", (long long)condition);
- return false;
- }
-
- for (const auto& link : links) {
- auto it = conditionTrackerMap.find(link.condition());
- if (it == conditionTrackerMap.end()) {
- ALOGW("cannot find Predicate \"%lld\" in the config", (long long)link.condition());
- return false;
- }
- allConditionTrackers[condition_it->second]->setSliced(true);
- allConditionTrackers[it->second]->setSliced(true);
- }
- conditionIndex = condition_it->second;
-
- // will create new vector if not exist before.
- auto& metricList = conditionToMetricMap[condition_it->second];
- metricList.push_back(metricIndex);
- return true;
-}
-
-// Initializes state data structures for a metric.
-// input:
-// [config]: the input config
-// [stateIds]: the slice_by_state ids for this metric
-// [stateAtomIdMap]: this map contains the mapping from all state ids to atom ids
-// [allStateGroupMaps]: this map contains the mapping from state ids and state
-// values to state group ids for all states
-// output:
-// [slicedStateAtoms]: a vector of atom ids of all the slice_by_states
-// [stateGroupMap]: this map should contain the mapping from states ids and state
-// values to state group ids for all states that this metric
-// is interested in
-bool handleMetricWithStates(
- const StatsdConfig& config, const ::google::protobuf::RepeatedField<int64_t>& stateIds,
- const unordered_map<int64_t, int>& stateAtomIdMap,
- const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
- vector<int>& slicedStateAtoms,
- unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) {
- for (const auto& stateId : stateIds) {
- auto it = stateAtomIdMap.find(stateId);
- if (it == stateAtomIdMap.end()) {
- ALOGW("cannot find State %" PRId64 " in the config", stateId);
- return false;
- }
- int atomId = it->second;
- slicedStateAtoms.push_back(atomId);
-
- auto stateIt = allStateGroupMaps.find(stateId);
- if (stateIt != allStateGroupMaps.end()) {
- stateGroupMap[atomId] = stateIt->second;
- }
- }
- return true;
-}
-
-bool handleMetricWithStateLink(const FieldMatcher& stateMatcher,
- const vector<Matcher>& dimensionsInWhat) {
- vector<Matcher> stateMatchers;
- translateFieldMatcher(stateMatcher, &stateMatchers);
-
- return subsetDimensions(stateMatchers, dimensionsInWhat);
-}
-
-// Validates a metricActivation and populates state.
-// EventActivationMap and EventDeactivationMap are supplied to a MetricProducer
-// to provide the producer with state about its activators and deactivators.
-// Returns false if there are errors.
-bool handleMetricActivation(
- const StatsdConfig& config,
- const int64_t metricId,
- const int metricIndex,
- const unordered_map<int64_t, int>& metricToActivationMap,
- const unordered_map<int64_t, int>& logTrackerMap,
- unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation,
- unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
- unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) {
- // Check if metric has an associated activation
- auto itr = metricToActivationMap.find(metricId);
- if (itr == metricToActivationMap.end()) return true;
-
- int activationIndex = itr->second;
- const MetricActivation& metricActivation = config.metric_activation(activationIndex);
-
- for (int i = 0; i < metricActivation.event_activation_size(); i++) {
- const EventActivation& activation = metricActivation.event_activation(i);
-
- auto itr = logTrackerMap.find(activation.atom_matcher_id());
- if (itr == logTrackerMap.end()) {
- ALOGE("Atom matcher not found for event activation.");
- return false;
- }
-
- ActivationType activationType = (activation.has_activation_type()) ?
- activation.activation_type() : metricActivation.activation_type();
- std::shared_ptr<Activation> activationWrapper = std::make_shared<Activation>(
- activationType, activation.ttl_seconds() * NS_PER_SEC);
-
- int atomMatcherIndex = itr->second;
- activationAtomTrackerToMetricMap[atomMatcherIndex].push_back(metricIndex);
- eventActivationMap.emplace(atomMatcherIndex, activationWrapper);
-
- if (activation.has_deactivation_atom_matcher_id()) {
- itr = logTrackerMap.find(activation.deactivation_atom_matcher_id());
- if (itr == logTrackerMap.end()) {
- ALOGE("Atom matcher not found for event deactivation.");
- return false;
- }
- int deactivationAtomMatcherIndex = itr->second;
- deactivationAtomTrackerToMetricMap[deactivationAtomMatcherIndex].push_back(metricIndex);
- eventDeactivationMap[deactivationAtomMatcherIndex].push_back(activationWrapper);
- }
- }
-
- metricsWithActivation.push_back(metricIndex);
- return true;
-}
-
-bool initLogTrackers(const StatsdConfig& config, const UidMap& uidMap,
- unordered_map<int64_t, int>& logTrackerMap,
- vector<sp<LogMatchingTracker>>& allAtomMatchers, set<int>& allTagIds) {
- vector<AtomMatcher> matcherConfigs;
- const int atomMatcherCount = config.atom_matcher_size();
- matcherConfigs.reserve(atomMatcherCount);
- allAtomMatchers.reserve(atomMatcherCount);
-
- for (int i = 0; i < atomMatcherCount; i++) {
- const AtomMatcher& logMatcher = config.atom_matcher(i);
-
- int index = allAtomMatchers.size();
- switch (logMatcher.contents_case()) {
- case AtomMatcher::ContentsCase::kSimpleAtomMatcher:
- allAtomMatchers.push_back(new SimpleLogMatchingTracker(
- logMatcher.id(), index, logMatcher.simple_atom_matcher(), uidMap));
- break;
- case AtomMatcher::ContentsCase::kCombination:
- allAtomMatchers.push_back(
- new CombinationLogMatchingTracker(logMatcher.id(), index));
- break;
- default:
- ALOGE("Matcher \"%lld\" malformed", (long long)logMatcher.id());
- return false;
- // continue;
- }
- if (logTrackerMap.find(logMatcher.id()) != logTrackerMap.end()) {
- ALOGE("Duplicate AtomMatcher found!");
- return false;
- }
- logTrackerMap[logMatcher.id()] = index;
- matcherConfigs.push_back(logMatcher);
- }
-
- vector<bool> stackTracker2(allAtomMatchers.size(), false);
- for (auto& matcher : allAtomMatchers) {
- if (!matcher->init(matcherConfigs, allAtomMatchers, logTrackerMap, stackTracker2)) {
- return false;
- }
- // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only.
- const set<int>& tagIds = matcher->getAtomIds();
- allTagIds.insert(tagIds.begin(), tagIds.end());
- }
- return true;
-}
-
-bool initConditions(const ConfigKey& key, const StatsdConfig& config,
- const unordered_map<int64_t, int>& logTrackerMap,
- unordered_map<int64_t, int>& conditionTrackerMap,
- vector<sp<ConditionTracker>>& allConditionTrackers,
- unordered_map<int, std::vector<int>>& trackerToConditionMap,
- vector<ConditionState>& initialConditionCache) {
- vector<Predicate> conditionConfigs;
- const int conditionTrackerCount = config.predicate_size();
- conditionConfigs.reserve(conditionTrackerCount);
- allConditionTrackers.reserve(conditionTrackerCount);
- initialConditionCache.reserve(conditionTrackerCount);
- std::fill(initialConditionCache.begin(), initialConditionCache.end(), ConditionState::kUnknown);
-
- for (int i = 0; i < conditionTrackerCount; i++) {
- const Predicate& condition = config.predicate(i);
- int index = allConditionTrackers.size();
- switch (condition.contents_case()) {
- case Predicate::ContentsCase::kSimplePredicate: {
- allConditionTrackers.push_back(new SimpleConditionTracker(
- key, condition.id(), index, condition.simple_predicate(), logTrackerMap));
- break;
- }
- case Predicate::ContentsCase::kCombination: {
- allConditionTrackers.push_back(
- new CombinationConditionTracker(condition.id(), index));
- break;
- }
- default:
- ALOGE("Predicate \"%lld\" malformed", (long long)condition.id());
- return false;
- }
- if (conditionTrackerMap.find(condition.id()) != conditionTrackerMap.end()) {
- ALOGE("Duplicate Predicate found!");
- return false;
- }
- conditionTrackerMap[condition.id()] = index;
- conditionConfigs.push_back(condition);
- }
-
- vector<bool> stackTracker(allConditionTrackers.size(), false);
- for (size_t i = 0; i < allConditionTrackers.size(); i++) {
- auto& conditionTracker = allConditionTrackers[i];
- if (!conditionTracker->init(conditionConfigs, allConditionTrackers, conditionTrackerMap,
- stackTracker, initialConditionCache)) {
- return false;
- }
- for (const int trackerIndex : conditionTracker->getLogTrackerIndex()) {
- auto& conditionList = trackerToConditionMap[trackerIndex];
- conditionList.push_back(i);
- }
- }
- return true;
-}
-
-bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
- unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps) {
- for (int i = 0; i < config.state_size(); i++) {
- const State& state = config.state(i);
- const int64_t stateId = state.id();
- stateAtomIdMap[stateId] = state.atom_id();
-
- const StateMap& stateMap = state.map();
- for (auto group : stateMap.group()) {
- for (auto value : group.value()) {
- allStateGroupMaps[stateId][value] = group.group_id();
- }
- }
- }
-
- return true;
-}
-
-bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs,
- const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
- const unordered_map<int64_t, int>& logTrackerMap,
- const unordered_map<int64_t, int>& conditionTrackerMap,
- const vector<sp<LogMatchingTracker>>& allAtomMatchers,
- const unordered_map<int64_t, int>& stateAtomIdMap,
- const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
- vector<sp<ConditionTracker>>& allConditionTrackers,
- const vector<ConditionState>& initialConditionCache,
- vector<sp<MetricProducer>>& allMetricProducers,
- unordered_map<int, vector<int>>& conditionToMetricMap,
- unordered_map<int, vector<int>>& trackerToMetricMap,
- unordered_map<int64_t, int>& metricMap, std::set<int64_t>& noReportMetricIds,
- unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation) {
- sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
- sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchers);
- const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
- config.event_metric_size() + config.gauge_metric_size() +
- config.value_metric_size();
- allMetricProducers.reserve(allMetricsCount);
-
- // Construct map from metric id to metric activation index. The map will be used to determine
- // the metric activation corresponding to a metric.
- unordered_map<int64_t, int> metricToActivationMap;
- for (int i = 0; i < config.metric_activation_size(); i++) {
- const MetricActivation& metricActivation = config.metric_activation(i);
- int64_t metricId = metricActivation.metric_id();
- if (metricToActivationMap.find(metricId) != metricToActivationMap.end()) {
- ALOGE("Metric %lld has multiple MetricActivations", (long long) metricId);
- return false;
- }
- metricToActivationMap.insert({metricId, i});
- }
-
- // Build MetricProducers for each metric defined in config.
- // build CountMetricProducer
- for (int i = 0; i < config.count_metric_size(); i++) {
- const CountMetric& metric = config.count_metric(i);
- if (!metric.has_what()) {
- ALOGW("cannot find \"what\" in CountMetric \"%lld\"", (long long)metric.id());
- return false;
- }
-
- int metricIndex = allMetricProducers.size();
- metricMap.insert({metric.id(), metricIndex});
- int trackerIndex;
- if (!handleMetricWithLogTrackers(metric.what(), metricIndex,
- metric.has_dimensions_in_what(),
- allAtomMatchers, logTrackerMap, trackerToMetricMap,
- trackerIndex)) {
- return false;
- }
-
- int conditionIndex = -1;
- if (metric.has_condition()) {
- if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
- metric.links(), allConditionTrackers, conditionIndex,
- conditionToMetricMap)) {
- return false;
- }
- } else {
- if (metric.links_size() > 0) {
- ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
- return false;
- }
- }
-
- std::vector<int> slicedStateAtoms;
- unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
- if (metric.slice_by_state_size() > 0) {
- if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
- allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
- return false;
- }
- } else {
- if (metric.state_link_size() > 0) {
- ALOGW("CountMetric has a MetricStateLink but doesn't have a slice_by_state");
- return false;
- }
- }
-
- unordered_map<int, shared_ptr<Activation>> eventActivationMap;
- unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
- bool success = handleMetricActivation(config, metric.id(), metricIndex,
- metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
- eventDeactivationMap);
- if (!success) return false;
-
- sp<MetricProducer> countProducer =
- new CountMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
- timeBaseTimeNs, currentTimeNs, eventActivationMap,
- eventDeactivationMap, slicedStateAtoms, stateGroupMap);
- allMetricProducers.push_back(countProducer);
- }
-
- // build DurationMetricProducer
- for (int i = 0; i < config.duration_metric_size(); i++) {
- int metricIndex = allMetricProducers.size();
- const DurationMetric& metric = config.duration_metric(i);
- metricMap.insert({metric.id(), metricIndex});
-
- auto what_it = conditionTrackerMap.find(metric.what());
- if (what_it == conditionTrackerMap.end()) {
- ALOGE("DurationMetric's \"what\" is invalid");
- return false;
- }
-
- const Predicate& durationWhat = config.predicate(what_it->second);
-
- if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) {
- ALOGE("DurationMetric's \"what\" must be a simple condition");
- return false;
- }
-
- const auto& simplePredicate = durationWhat.simple_predicate();
-
- bool nesting = simplePredicate.count_nesting();
-
- int trackerIndices[3] = {-1, -1, -1};
- if (!simplePredicate.has_start() ||
- !handleMetricWithLogTrackers(simplePredicate.start(), metricIndex,
- metric.has_dimensions_in_what(), allAtomMatchers,
- logTrackerMap, trackerToMetricMap, trackerIndices[0])) {
- ALOGE("Duration metrics must specify a valid the start event matcher");
- return false;
- }
-
- if (simplePredicate.has_stop() &&
- !handleMetricWithLogTrackers(simplePredicate.stop(), metricIndex,
- metric.has_dimensions_in_what(), allAtomMatchers,
- logTrackerMap, trackerToMetricMap, trackerIndices[1])) {
- return false;
- }
-
- if (simplePredicate.has_stop_all() &&
- !handleMetricWithLogTrackers(simplePredicate.stop_all(), metricIndex,
- metric.has_dimensions_in_what(), allAtomMatchers,
- logTrackerMap, trackerToMetricMap, trackerIndices[2])) {
- return false;
- }
-
- FieldMatcher internalDimensions = simplePredicate.dimensions();
-
- int conditionIndex = -1;
-
- if (metric.has_condition()) {
- bool good = handleMetricWithConditions(
- metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
- allConditionTrackers, conditionIndex, conditionToMetricMap);
- if (!good) {
- return false;
- }
- } else {
- if (metric.links_size() > 0) {
- ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
- return false;
- }
- }
-
- std::vector<int> slicedStateAtoms;
- unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
- if (metric.slice_by_state_size() > 0) {
- if (metric.aggregation_type() == DurationMetric::MAX_SPARSE) {
- ALOGE("DurationMetric with aggregation type MAX_SPARSE cannot be sliced by state");
- return false;
- }
- if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
- allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
- return false;
- }
- } else {
- if (metric.state_link_size() > 0) {
- ALOGW("DurationMetric has a MetricStateLink but doesn't have a sliced state");
- return false;
- }
- }
-
- // Check that all metric state links are a subset of dimensions_in_what fields.
- std::vector<Matcher> dimensionsInWhat;
- translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
- for (const auto& stateLink : metric.state_link()) {
- if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) {
- return false;
- }
- }
-
- unordered_map<int, shared_ptr<Activation>> eventActivationMap;
- unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
- bool success = handleMetricActivation(config, metric.id(), metricIndex,
- metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
- eventDeactivationMap);
- if (!success) return false;
-
- sp<MetricProducer> durationMetric = new DurationMetricProducer(
- key, metric, conditionIndex, initialConditionCache, trackerIndices[0],
- trackerIndices[1], trackerIndices[2], nesting, wizard, internalDimensions,
- timeBaseTimeNs, currentTimeNs, eventActivationMap, eventDeactivationMap,
- slicedStateAtoms, stateGroupMap);
-
- allMetricProducers.push_back(durationMetric);
- }
-
- // build EventMetricProducer
- for (int i = 0; i < config.event_metric_size(); i++) {
- int metricIndex = allMetricProducers.size();
- const EventMetric& metric = config.event_metric(i);
- metricMap.insert({metric.id(), metricIndex});
- if (!metric.has_id() || !metric.has_what()) {
- ALOGW("cannot find the metric name or what in config");
- return false;
- }
- int trackerIndex;
- if (!handleMetricWithLogTrackers(metric.what(), metricIndex, false, allAtomMatchers,
- logTrackerMap, trackerToMetricMap, trackerIndex)) {
- return false;
- }
-
- int conditionIndex = -1;
- if (metric.has_condition()) {
- bool good = handleMetricWithConditions(
- metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
- allConditionTrackers, conditionIndex, conditionToMetricMap);
- if (!good) {
- return false;
- }
- } else {
- if (metric.links_size() > 0) {
- ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
- return false;
- }
- }
-
- unordered_map<int, shared_ptr<Activation>> eventActivationMap;
- unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
- bool success = handleMetricActivation(config, metric.id(), metricIndex,
- metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
- eventDeactivationMap);
- if (!success) return false;
-
- sp<MetricProducer> eventMetric =
- new EventMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
- timeBaseTimeNs, eventActivationMap, eventDeactivationMap);
-
- allMetricProducers.push_back(eventMetric);
- }
-
- // build ValueMetricProducer
- for (int i = 0; i < config.value_metric_size(); i++) {
- const ValueMetric& metric = config.value_metric(i);
- if (!metric.has_what()) {
- ALOGW("cannot find \"what\" in ValueMetric \"%lld\"", (long long)metric.id());
- return false;
- }
- if (!metric.has_value_field()) {
- ALOGW("cannot find \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id());
- return false;
- }
- std::vector<Matcher> fieldMatchers;
- translateFieldMatcher(metric.value_field(), &fieldMatchers);
- if (fieldMatchers.size() < 1) {
- ALOGW("incorrect \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id());
- return false;
- }
-
- int metricIndex = allMetricProducers.size();
- metricMap.insert({metric.id(), metricIndex});
- int trackerIndex;
- if (!handleMetricWithLogTrackers(metric.what(), metricIndex,
- metric.has_dimensions_in_what(),
- allAtomMatchers, logTrackerMap, trackerToMetricMap,
- trackerIndex)) {
- return false;
- }
-
- sp<LogMatchingTracker> atomMatcher = allAtomMatchers.at(trackerIndex);
- // If it is pulled atom, it should be simple matcher with one tagId.
- if (atomMatcher->getAtomIds().size() != 1) {
- return false;
- }
- int atomTagId = *(atomMatcher->getAtomIds().begin());
- int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
-
- int conditionIndex = -1;
- if (metric.has_condition()) {
- bool good = handleMetricWithConditions(
- metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
- allConditionTrackers, conditionIndex, conditionToMetricMap);
- if (!good) {
- return false;
- }
- } else {
- if (metric.links_size() > 0) {
- ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
- return false;
- }
- }
-
- std::vector<int> slicedStateAtoms;
- unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
- if (metric.slice_by_state_size() > 0) {
- if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
- allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
- return false;
- }
- } else {
- if (metric.state_link_size() > 0) {
- ALOGW("ValueMetric has a MetricStateLink but doesn't have a sliced state");
- return false;
- }
- }
-
- // Check that all metric state links are a subset of dimensions_in_what fields.
- std::vector<Matcher> dimensionsInWhat;
- translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
- for (const auto& stateLink : metric.state_link()) {
- if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) {
- return false;
- }
- }
-
- unordered_map<int, shared_ptr<Activation>> eventActivationMap;
- unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
- bool success = handleMetricActivation(
- config, metric.id(), metricIndex, metricToActivationMap, logTrackerMap,
- activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, eventActivationMap, eventDeactivationMap);
- if (!success) return false;
-
- sp<MetricProducer> valueProducer = new ValueMetricProducer(
- key, metric, conditionIndex, initialConditionCache, wizard, trackerIndex,
- matcherWizard, pullTagId, timeBaseTimeNs, currentTimeNs, pullerManager,
- eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap);
- allMetricProducers.push_back(valueProducer);
- }
-
- // Gauge metrics.
- for (int i = 0; i < config.gauge_metric_size(); i++) {
- const GaugeMetric& metric = config.gauge_metric(i);
- if (!metric.has_what()) {
- ALOGW("cannot find \"what\" in GaugeMetric \"%lld\"", (long long)metric.id());
- return false;
- }
-
- if ((!metric.gauge_fields_filter().has_include_all() ||
- (metric.gauge_fields_filter().include_all() == false)) &&
- !hasLeafNode(metric.gauge_fields_filter().fields())) {
- ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
- return false;
- }
- if ((metric.gauge_fields_filter().has_include_all() &&
- metric.gauge_fields_filter().include_all() == true) &&
- hasLeafNode(metric.gauge_fields_filter().fields())) {
- ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
- return false;
- }
-
- int metricIndex = allMetricProducers.size();
- metricMap.insert({metric.id(), metricIndex});
- int trackerIndex;
- if (!handleMetricWithLogTrackers(metric.what(), metricIndex,
- metric.has_dimensions_in_what(),
- allAtomMatchers, logTrackerMap, trackerToMetricMap,
- trackerIndex)) {
- return false;
- }
-
- sp<LogMatchingTracker> atomMatcher = allAtomMatchers.at(trackerIndex);
- // For GaugeMetric atom, it should be simple matcher with one tagId.
- if (atomMatcher->getAtomIds().size() != 1) {
- return false;
- }
- int atomTagId = *(atomMatcher->getAtomIds().begin());
- int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
-
- int triggerTrackerIndex;
- int triggerAtomId = -1;
- if (metric.has_trigger_event()) {
- if (pullTagId == -1) {
- ALOGW("Pull atom not specified for trigger");
- return false;
- }
- // event_trigger should be used with FIRST_N_SAMPLES
- if (metric.sampling_type() != GaugeMetric::FIRST_N_SAMPLES) {
- return false;
- }
- if (!handlePullMetricTriggerWithLogTrackers(metric.trigger_event(), metricIndex,
- allAtomMatchers, logTrackerMap,
- trackerToMetricMap, triggerTrackerIndex)) {
- return false;
- }
- sp<LogMatchingTracker> triggerAtomMatcher = allAtomMatchers.at(triggerTrackerIndex);
- triggerAtomId = *(triggerAtomMatcher->getAtomIds().begin());
- }
-
- if (!metric.has_trigger_event() && pullTagId != -1 &&
- metric.sampling_type() == GaugeMetric::FIRST_N_SAMPLES) {
- ALOGW("FIRST_N_SAMPLES is only for pushed event or pull_on_trigger");
- return false;
- }
-
- int conditionIndex = -1;
- if (metric.has_condition()) {
- bool good = handleMetricWithConditions(
- metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
- allConditionTrackers, conditionIndex, conditionToMetricMap);
- if (!good) {
- return false;
- }
- } else {
- if (metric.links_size() > 0) {
- ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
- return false;
- }
- }
-
- unordered_map<int, shared_ptr<Activation>> eventActivationMap;
- unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
- bool success = handleMetricActivation(config, metric.id(), metricIndex,
- metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
- eventDeactivationMap);
- if (!success) return false;
-
- sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(
- key, metric, conditionIndex, initialConditionCache, wizard, trackerIndex,
- matcherWizard, pullTagId, triggerAtomId, atomTagId, timeBaseTimeNs, currentTimeNs,
- pullerManager, eventActivationMap, eventDeactivationMap);
- allMetricProducers.push_back(gaugeProducer);
- }
- for (int i = 0; i < config.no_report_metric_size(); ++i) {
- const auto no_report_metric = config.no_report_metric(i);
- if (metricMap.find(no_report_metric) == metricMap.end()) {
- ALOGW("no_report_metric %" PRId64 " not exist", no_report_metric);
- return false;
- }
- noReportMetricIds.insert(no_report_metric);
- }
-
- const set<int> whitelistedAtomIds(config.whitelisted_atom_ids().begin(),
- config.whitelisted_atom_ids().end());
- for (const auto& it : allMetricProducers) {
- // Register metrics to StateTrackers
- for (int atomId : it->getSlicedStateAtoms()) {
- // Register listener for non-whitelisted atoms only. Using whitelisted atom as a sliced
- // state atom is not allowed.
- if (whitelistedAtomIds.find(atomId) == whitelistedAtomIds.end()) {
- StateManager::getInstance().registerListener(atomId, it);
- } else {
- return false;
- }
- }
- }
- return true;
-}
-
-bool initAlerts(const StatsdConfig& config,
- const unordered_map<int64_t, int>& metricProducerMap,
- unordered_map<int64_t, int>& alertTrackerMap,
- const sp<AlarmMonitor>& anomalyAlarmMonitor,
- vector<sp<MetricProducer>>& allMetricProducers,
- vector<sp<AnomalyTracker>>& allAnomalyTrackers) {
- for (int i = 0; i < config.alert_size(); i++) {
- const Alert& alert = config.alert(i);
- const auto& itr = metricProducerMap.find(alert.metric_id());
- if (itr == metricProducerMap.end()) {
- ALOGW("alert \"%lld\" has unknown metric id: \"%lld\"", (long long)alert.id(),
- (long long)alert.metric_id());
- return false;
- }
- if (!alert.has_trigger_if_sum_gt()) {
- ALOGW("invalid alert: missing threshold");
- return false;
- }
- if (alert.trigger_if_sum_gt() < 0 || alert.num_buckets() <= 0) {
- ALOGW("invalid alert: threshold=%f num_buckets= %d",
- alert.trigger_if_sum_gt(), alert.num_buckets());
- return false;
- }
- const int metricIndex = itr->second;
- sp<MetricProducer> metric = allMetricProducers[metricIndex];
- sp<AnomalyTracker> anomalyTracker = metric->addAnomalyTracker(alert, anomalyAlarmMonitor);
- if (anomalyTracker == nullptr) {
- // The ALOGW for this invalid alert was already displayed in addAnomalyTracker().
- return false;
- }
- alertTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size()));
- allAnomalyTrackers.push_back(anomalyTracker);
- }
- for (int i = 0; i < config.subscription_size(); ++i) {
- const Subscription& subscription = config.subscription(i);
- if (subscription.rule_type() != Subscription::ALERT) {
- continue;
- }
- if (subscription.subscriber_information_case() ==
- Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) {
- ALOGW("subscription \"%lld\" has no subscriber info.\"",
- (long long)subscription.id());
- return false;
- }
- const auto& itr = alertTrackerMap.find(subscription.rule_id());
- if (itr == alertTrackerMap.end()) {
- ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"",
- (long long)subscription.id(), (long long)subscription.rule_id());
- return false;
- }
- const int anomalyTrackerIndex = itr->second;
- allAnomalyTrackers[anomalyTrackerIndex]->addSubscription(subscription);
- }
- return true;
-}
-
-bool initAlarms(const StatsdConfig& config, const ConfigKey& key,
- const sp<AlarmMonitor>& periodicAlarmMonitor,
- const int64_t timeBaseNs, const int64_t currentTimeNs,
- vector<sp<AlarmTracker>>& allAlarmTrackers) {
- unordered_map<int64_t, int> alarmTrackerMap;
- int64_t startMillis = timeBaseNs / 1000 / 1000;
- int64_t currentTimeMillis = currentTimeNs / 1000 /1000;
- for (int i = 0; i < config.alarm_size(); i++) {
- const Alarm& alarm = config.alarm(i);
- if (alarm.offset_millis() <= 0) {
- ALOGW("Alarm offset_millis should be larger than 0.");
- return false;
- }
- if (alarm.period_millis() <= 0) {
- ALOGW("Alarm period_millis should be larger than 0.");
- return false;
- }
- alarmTrackerMap.insert(std::make_pair(alarm.id(), allAlarmTrackers.size()));
- allAlarmTrackers.push_back(
- new AlarmTracker(startMillis, currentTimeMillis,
- alarm, key, periodicAlarmMonitor));
- }
- for (int i = 0; i < config.subscription_size(); ++i) {
- const Subscription& subscription = config.subscription(i);
- if (subscription.rule_type() != Subscription::ALARM) {
- continue;
- }
- if (subscription.subscriber_information_case() ==
- Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) {
- ALOGW("subscription \"%lld\" has no subscriber info.\"",
- (long long)subscription.id());
- return false;
- }
- const auto& itr = alarmTrackerMap.find(subscription.rule_id());
- if (itr == alarmTrackerMap.end()) {
- ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"",
- (long long)subscription.id(), (long long)subscription.rule_id());
- return false;
- }
- const int trackerIndex = itr->second;
- allAlarmTrackers[trackerIndex]->addSubscription(subscription);
- }
- return true;
-}
-
-bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap,
- const sp<StatsPullerManager>& pullerManager,
- const sp<AlarmMonitor>& anomalyAlarmMonitor,
- const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
- const int64_t currentTimeNs, set<int>& allTagIds,
- vector<sp<LogMatchingTracker>>& allAtomMatchers,
- vector<sp<ConditionTracker>>& allConditionTrackers,
- vector<sp<MetricProducer>>& allMetricProducers,
- vector<sp<AnomalyTracker>>& allAnomalyTrackers,
- vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers,
- unordered_map<int, std::vector<int>>& conditionToMetricMap,
- unordered_map<int, std::vector<int>>& trackerToMetricMap,
- unordered_map<int, std::vector<int>>& trackerToConditionMap,
- unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- unordered_map<int64_t, int>& alertTrackerMap,
- vector<int>& metricsWithActivation,
- std::set<int64_t>& noReportMetricIds) {
- unordered_map<int64_t, int> logTrackerMap;
- unordered_map<int64_t, int> conditionTrackerMap;
- vector<ConditionState> initialConditionCache;
- unordered_map<int64_t, int> metricProducerMap;
- unordered_map<int64_t, int> stateAtomIdMap;
- unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
-
- if (!initLogTrackers(config, uidMap, logTrackerMap, allAtomMatchers, allTagIds)) {
- ALOGE("initLogMatchingTrackers failed");
- return false;
- }
- VLOG("initLogMatchingTrackers succeed...");
-
- if (!initConditions(key, config, logTrackerMap, conditionTrackerMap, allConditionTrackers,
- trackerToConditionMap, initialConditionCache)) {
- ALOGE("initConditionTrackers failed");
- return false;
- }
-
- if (!initStates(config, stateAtomIdMap, allStateGroupMaps)) {
- ALOGE("initStates failed");
- return false;
- }
- if (!initMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager, logTrackerMap,
- conditionTrackerMap, allAtomMatchers, stateAtomIdMap, allStateGroupMaps,
- allConditionTrackers, initialConditionCache, allMetricProducers,
- conditionToMetricMap, trackerToMetricMap, metricProducerMap, noReportMetricIds,
- activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation)) {
- ALOGE("initMetricProducers failed");
- return false;
- }
- if (!initAlerts(config, metricProducerMap, alertTrackerMap, anomalyAlarmMonitor,
- allMetricProducers, allAnomalyTrackers)) {
- ALOGE("initAlerts failed");
- return false;
- }
- if (!initAlarms(config, key, periodicAlarmMonitor,
- timeBaseNs, currentTimeNs, allPeriodicAlarmTrackers)) {
- ALOGE("initAlarms failed");
- return false;
- }
-
- return true;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/bin/src/metrics/metrics_manager_util.h b/bin/src/metrics/metrics_manager_util.h
deleted file mode 100644
index 96b5c26..0000000
--- a/bin/src/metrics/metrics_manager_util.h
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#pragma once
-
-#include <set>
-#include <unordered_map>
-#include <vector>
-
-#include "../anomaly/AlarmTracker.h"
-#include "../condition/ConditionTracker.h"
-#include "../external/StatsPullerManager.h"
-#include "../matchers/LogMatchingTracker.h"
-#include "../metrics/MetricProducer.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// Helper functions for MetricsManager to initialize from StatsdConfig.
-// *Note*: only initStatsdConfig() should be called from outside.
-// All other functions are intermediate
-// steps, created to make unit tests easier. And most of the parameters in these
-// functions are temporary objects in the initialization phase.
-
-// Initialize the LogMatchingTrackers.
-// input:
-// [key]: the config key that this config belongs to
-// [config]: the input StatsdConfig
-// output:
-// [logTrackerMap]: this map should contain matcher name to index mapping
-// [allAtomMatchers]: should store the sp to all the LogMatchingTracker
-// [allTagIds]: contains the set of all interesting tag ids to this config.
-bool initLogTrackers(const StatsdConfig& config,
- const UidMap& uidMap,
- std::unordered_map<int64_t, int>& logTrackerMap,
- std::vector<sp<LogMatchingTracker>>& allAtomMatchers,
- std::set<int>& allTagIds);
-
-// Initialize ConditionTrackers
-// input:
-// [key]: the config key that this config belongs to
-// [config]: the input config
-// [logTrackerMap]: LogMatchingTracker name to index mapping from previous step.
-// output:
-// [conditionTrackerMap]: this map should contain condition name to index mapping
-// [allConditionTrackers]: stores the sp to all the ConditionTrackers
-// [trackerToConditionMap]: contain the mapping from index of
-// log tracker to condition trackers that use the log tracker
-// [initialConditionCache]: stores the initial conditions for each ConditionTracker
-bool initConditions(const ConfigKey& key, const StatsdConfig& config,
- const std::unordered_map<int64_t, int>& logTrackerMap,
- std::unordered_map<int64_t, int>& conditionTrackerMap,
- std::vector<sp<ConditionTracker>>& allConditionTrackers,
- std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
- std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks,
- std::vector<ConditionState>& initialConditionCache);
-
-// Initialize State maps using State protos in the config. These maps will
-// eventually be passed to MetricProducers to initialize their state info.
-// input:
-// [config]: the input config
-// output:
-// [stateAtomIdMap]: this map should contain the mapping from state ids to atom ids
-// [allStateGroupMaps]: this map should contain the mapping from states ids and state
-// values to state group ids for all states
-bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
- unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps);
-
-// Initialize MetricProducers.
-// input:
-// [key]: the config key that this config belongs to
-// [config]: the input config
-// [timeBaseSec]: start time base for all metrics
-// [logTrackerMap]: LogMatchingTracker name to index mapping from previous step.
-// [conditionTrackerMap]: condition name to index mapping
-// [stateAtomIdMap]: contains the mapping from state ids to atom ids
-// [allStateGroupMaps]: contains the mapping from atom ids and state values to
-// state group ids for all states
-// output:
-// [allMetricProducers]: contains the list of sp to the MetricProducers created.
-// [conditionToMetricMap]: contains the mapping from condition tracker index to
-// the list of MetricProducer index
-// [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
-bool initMetrics(
- const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs,
- const int64_t currentTimeNs, UidMap& uidMap, const sp<StatsPullerManager>& pullerManager,
- const std::unordered_map<int64_t, int>& logTrackerMap,
- const std::unordered_map<int64_t, int>& conditionTrackerMap,
- const std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks,
- const vector<sp<LogMatchingTracker>>& allAtomMatchers,
- const unordered_map<int64_t, int>& stateAtomIdMap,
- const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
- vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::vector<ConditionState>& initialConditionCache,
- std::vector<sp<MetricProducer>>& allMetricProducers,
- std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
- std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
- std::set<int64_t>& noReportMetricIds,
- std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::vector<int>& metricsWithActivation);
-
-// Initialize MetricsManager from StatsdConfig.
-// Parameters are the members of MetricsManager. See MetricsManager for declaration.
-bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap,
- const sp<StatsPullerManager>& pullerManager,
- const sp<AlarmMonitor>& anomalyAlarmMonitor,
- const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
- const int64_t currentTimeNs, std::set<int>& allTagIds,
- std::vector<sp<LogMatchingTracker>>& allAtomMatchers,
- std::vector<sp<ConditionTracker>>& allConditionTrackers,
- std::vector<sp<MetricProducer>>& allMetricProducers,
- vector<sp<AnomalyTracker>>& allAnomalyTrackers,
- vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers,
- std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
- std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
- unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::unordered_map<int64_t, int>& alertTrackerMap,
- vector<int>& metricsWithActivation,
- std::set<int64_t>& noReportMetricIds);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/bin/src/metrics/parsing_utils/config_update_utils.cpp b/bin/src/metrics/parsing_utils/config_update_utils.cpp
new file mode 100644
index 0000000..39789cd
--- /dev/null
+++ b/bin/src/metrics/parsing_utils/config_update_utils.cpp
@@ -0,0 +1,1118 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define DEBUG false // STOPSHIP if true
+#include "Log.h"
+
+#include "config_update_utils.h"
+
+#include "external/StatsPullerManager.h"
+#include "hash.h"
+#include "matchers/EventMatcherWizard.h"
+#include "metrics_manager_util.h"
+
+using google::protobuf::MessageLite;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Recursive function to determine if a matcher needs to be updated. Populates matcherToUpdate.
+// Returns whether the function was successful or not.
+bool determineMatcherUpdateStatus(const StatsdConfig& config, const int matcherIdx,
+ const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ vector<UpdateStatus>& matchersToUpdate,
+ vector<bool>& cycleTracker) {
+ // Have already examined this matcher.
+ if (matchersToUpdate[matcherIdx] != UPDATE_UNKNOWN) {
+ return true;
+ }
+
+ const AtomMatcher& matcher = config.atom_matcher(matcherIdx);
+ int64_t id = matcher.id();
+ // Check if new matcher.
+ const auto& oldAtomMatchingTrackerIt = oldAtomMatchingTrackerMap.find(id);
+ if (oldAtomMatchingTrackerIt == oldAtomMatchingTrackerMap.end()) {
+ matchersToUpdate[matcherIdx] = UPDATE_NEW;
+ return true;
+ }
+
+ // This is an existing matcher. Check if it has changed.
+ string serializedMatcher;
+ if (!matcher.SerializeToString(&serializedMatcher)) {
+ ALOGE("Unable to serialize matcher %lld", (long long)id);
+ return false;
+ }
+ uint64_t newProtoHash = Hash64(serializedMatcher);
+ if (newProtoHash != oldAtomMatchingTrackers[oldAtomMatchingTrackerIt->second]->getProtoHash()) {
+ matchersToUpdate[matcherIdx] = UPDATE_REPLACE;
+ return true;
+ }
+
+ switch (matcher.contents_case()) {
+ case AtomMatcher::ContentsCase::kSimpleAtomMatcher: {
+ matchersToUpdate[matcherIdx] = UPDATE_PRESERVE;
+ return true;
+ }
+ case AtomMatcher::ContentsCase::kCombination: {
+ // Recurse to check if children have changed.
+ cycleTracker[matcherIdx] = true;
+ UpdateStatus status = UPDATE_PRESERVE;
+ for (const int64_t childMatcherId : matcher.combination().matcher()) {
+ const auto& childIt = newAtomMatchingTrackerMap.find(childMatcherId);
+ if (childIt == newAtomMatchingTrackerMap.end()) {
+ ALOGW("Matcher %lld not found in the config", (long long)childMatcherId);
+ return false;
+ }
+ const int childIdx = childIt->second;
+ if (cycleTracker[childIdx]) {
+ ALOGE("Cycle detected in matcher config");
+ return false;
+ }
+ if (!determineMatcherUpdateStatus(
+ config, childIdx, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers,
+ newAtomMatchingTrackerMap, matchersToUpdate, cycleTracker)) {
+ return false;
+ }
+
+ if (matchersToUpdate[childIdx] == UPDATE_REPLACE) {
+ status = UPDATE_REPLACE;
+ break;
+ }
+ }
+ matchersToUpdate[matcherIdx] = status;
+ cycleTracker[matcherIdx] = false;
+ return true;
+ }
+ default: {
+ ALOGE("Matcher \"%lld\" malformed", (long long)id);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool updateAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
+ const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
+ set<int>& allTagIds,
+ unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
+ set<int64_t>& replacedMatchers) {
+ const int atomMatcherCount = config.atom_matcher_size();
+ vector<AtomMatcher> matcherProtos;
+ matcherProtos.reserve(atomMatcherCount);
+ newAtomMatchingTrackers.reserve(atomMatcherCount);
+
+ // Maps matcher id to their position in the config. For fast lookup of dependencies.
+ for (int i = 0; i < atomMatcherCount; i++) {
+ const AtomMatcher& matcher = config.atom_matcher(i);
+ if (newAtomMatchingTrackerMap.find(matcher.id()) != newAtomMatchingTrackerMap.end()) {
+ ALOGE("Duplicate atom matcher found for id %lld", (long long)matcher.id());
+ return false;
+ }
+ newAtomMatchingTrackerMap[matcher.id()] = i;
+ matcherProtos.push_back(matcher);
+ }
+
+ // For combination matchers, we need to determine if any children need to be updated.
+ vector<UpdateStatus> matchersToUpdate(atomMatcherCount, UPDATE_UNKNOWN);
+ vector<bool> cycleTracker(atomMatcherCount, false);
+ for (int i = 0; i < atomMatcherCount; i++) {
+ if (!determineMatcherUpdateStatus(config, i, oldAtomMatchingTrackerMap,
+ oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
+ matchersToUpdate, cycleTracker)) {
+ return false;
+ }
+ }
+
+ for (int i = 0; i < atomMatcherCount; i++) {
+ const AtomMatcher& matcher = config.atom_matcher(i);
+ const int64_t id = matcher.id();
+ switch (matchersToUpdate[i]) {
+ case UPDATE_PRESERVE: {
+ const auto& oldAtomMatchingTrackerIt = oldAtomMatchingTrackerMap.find(id);
+ if (oldAtomMatchingTrackerIt == oldAtomMatchingTrackerMap.end()) {
+ ALOGE("Could not find AtomMatcher %lld in the previous config, but expected it "
+ "to be there",
+ (long long)id);
+ return false;
+ }
+ const sp<AtomMatchingTracker>& tracker =
+ oldAtomMatchingTrackers[oldAtomMatchingTrackerIt->second];
+ if (!tracker->onConfigUpdated(matcherProtos[i], i, newAtomMatchingTrackerMap)) {
+ ALOGW("Config update failed for matcher %lld", (long long)id);
+ return false;
+ }
+ newAtomMatchingTrackers.push_back(tracker);
+ break;
+ }
+ case UPDATE_REPLACE:
+ replacedMatchers.insert(id);
+ [[fallthrough]]; // Intentionally fallthrough to create the new matcher.
+ case UPDATE_NEW: {
+ sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(matcher, i, uidMap);
+ if (tracker == nullptr) {
+ return false;
+ }
+ newAtomMatchingTrackers.push_back(tracker);
+ break;
+ }
+ default: {
+ ALOGE("Matcher \"%lld\" update state is unknown. This should never happen",
+ (long long)id);
+ return false;
+ }
+ }
+ }
+
+ std::fill(cycleTracker.begin(), cycleTracker.end(), false);
+ for (auto& matcher : newAtomMatchingTrackers) {
+ if (!matcher->init(matcherProtos, newAtomMatchingTrackers, newAtomMatchingTrackerMap,
+ cycleTracker)) {
+ return false;
+ }
+ // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only.
+ const set<int>& tagIds = matcher->getAtomIds();
+ allTagIds.insert(tagIds.begin(), tagIds.end());
+ }
+
+ return true;
+}
+
+// Recursive function to determine if a condition needs to be updated. Populates conditionsToUpdate.
+// Returns whether the function was successful or not.
+bool determineConditionUpdateStatus(const StatsdConfig& config, const int conditionIdx,
+ const unordered_map<int64_t, int>& oldConditionTrackerMap,
+ const vector<sp<ConditionTracker>>& oldConditionTrackers,
+ const unordered_map<int64_t, int>& newConditionTrackerMap,
+ const set<int64_t>& replacedMatchers,
+ vector<UpdateStatus>& conditionsToUpdate,
+ vector<bool>& cycleTracker) {
+ // Have already examined this condition.
+ if (conditionsToUpdate[conditionIdx] != UPDATE_UNKNOWN) {
+ return true;
+ }
+
+ const Predicate& predicate = config.predicate(conditionIdx);
+ int64_t id = predicate.id();
+ // Check if new condition.
+ const auto& oldConditionTrackerIt = oldConditionTrackerMap.find(id);
+ if (oldConditionTrackerIt == oldConditionTrackerMap.end()) {
+ conditionsToUpdate[conditionIdx] = UPDATE_NEW;
+ return true;
+ }
+
+ // This is an existing condition. Check if it has changed.
+ string serializedCondition;
+ if (!predicate.SerializeToString(&serializedCondition)) {
+ ALOGE("Unable to serialize matcher %lld", (long long)id);
+ return false;
+ }
+ uint64_t newProtoHash = Hash64(serializedCondition);
+ if (newProtoHash != oldConditionTrackers[oldConditionTrackerIt->second]->getProtoHash()) {
+ conditionsToUpdate[conditionIdx] = UPDATE_REPLACE;
+ return true;
+ }
+
+ switch (predicate.contents_case()) {
+ case Predicate::ContentsCase::kSimplePredicate: {
+ // Need to check if any of the underlying matchers changed.
+ const SimplePredicate& simplePredicate = predicate.simple_predicate();
+ if (simplePredicate.has_start()) {
+ if (replacedMatchers.find(simplePredicate.start()) != replacedMatchers.end()) {
+ conditionsToUpdate[conditionIdx] = UPDATE_REPLACE;
+ return true;
+ }
+ }
+ if (simplePredicate.has_stop()) {
+ if (replacedMatchers.find(simplePredicate.stop()) != replacedMatchers.end()) {
+ conditionsToUpdate[conditionIdx] = UPDATE_REPLACE;
+ return true;
+ }
+ }
+ if (simplePredicate.has_stop_all()) {
+ if (replacedMatchers.find(simplePredicate.stop_all()) != replacedMatchers.end()) {
+ conditionsToUpdate[conditionIdx] = UPDATE_REPLACE;
+ return true;
+ }
+ }
+ conditionsToUpdate[conditionIdx] = UPDATE_PRESERVE;
+ return true;
+ }
+ case Predicate::ContentsCase::kCombination: {
+ // Need to recurse on the children to see if any of the child predicates changed.
+ cycleTracker[conditionIdx] = true;
+ UpdateStatus status = UPDATE_PRESERVE;
+ for (const int64_t childPredicateId : predicate.combination().predicate()) {
+ const auto& childIt = newConditionTrackerMap.find(childPredicateId);
+ if (childIt == newConditionTrackerMap.end()) {
+ ALOGW("Predicate %lld not found in the config", (long long)childPredicateId);
+ return false;
+ }
+ const int childIdx = childIt->second;
+ if (cycleTracker[childIdx]) {
+ ALOGE("Cycle detected in predicate config");
+ return false;
+ }
+ if (!determineConditionUpdateStatus(config, childIdx, oldConditionTrackerMap,
+ oldConditionTrackers, newConditionTrackerMap,
+ replacedMatchers, conditionsToUpdate,
+ cycleTracker)) {
+ return false;
+ }
+
+ if (conditionsToUpdate[childIdx] == UPDATE_REPLACE) {
+ status = UPDATE_REPLACE;
+ break;
+ }
+ }
+ conditionsToUpdate[conditionIdx] = status;
+ cycleTracker[conditionIdx] = false;
+ return true;
+ }
+ default: {
+ ALOGE("Predicate \"%lld\" malformed", (long long)id);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool updateConditions(const ConfigKey& key, const StatsdConfig& config,
+ const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ const set<int64_t>& replacedMatchers,
+ const unordered_map<int64_t, int>& oldConditionTrackerMap,
+ const vector<sp<ConditionTracker>>& oldConditionTrackers,
+ unordered_map<int64_t, int>& newConditionTrackerMap,
+ vector<sp<ConditionTracker>>& newConditionTrackers,
+ unordered_map<int, vector<int>>& trackerToConditionMap,
+ vector<ConditionState>& conditionCache, set<int64_t>& replacedConditions) {
+ vector<Predicate> conditionProtos;
+ const int conditionTrackerCount = config.predicate_size();
+ conditionProtos.reserve(conditionTrackerCount);
+ newConditionTrackers.reserve(conditionTrackerCount);
+ conditionCache.assign(conditionTrackerCount, ConditionState::kNotEvaluated);
+
+ for (int i = 0; i < conditionTrackerCount; i++) {
+ const Predicate& condition = config.predicate(i);
+ if (newConditionTrackerMap.find(condition.id()) != newConditionTrackerMap.end()) {
+ ALOGE("Duplicate Predicate found!");
+ return false;
+ }
+ newConditionTrackerMap[condition.id()] = i;
+ conditionProtos.push_back(condition);
+ }
+
+ vector<UpdateStatus> conditionsToUpdate(conditionTrackerCount, UPDATE_UNKNOWN);
+ vector<bool> cycleTracker(conditionTrackerCount, false);
+ for (int i = 0; i < conditionTrackerCount; i++) {
+ if (!determineConditionUpdateStatus(config, i, oldConditionTrackerMap, oldConditionTrackers,
+ newConditionTrackerMap, replacedMatchers,
+ conditionsToUpdate, cycleTracker)) {
+ return false;
+ }
+ }
+
+ // Update status has been determined for all conditions. Now perform the update.
+ set<int> preservedConditions;
+ for (int i = 0; i < conditionTrackerCount; i++) {
+ const Predicate& predicate = config.predicate(i);
+ const int64_t id = predicate.id();
+ switch (conditionsToUpdate[i]) {
+ case UPDATE_PRESERVE: {
+ preservedConditions.insert(i);
+ const auto& oldConditionTrackerIt = oldConditionTrackerMap.find(id);
+ if (oldConditionTrackerIt == oldConditionTrackerMap.end()) {
+ ALOGE("Could not find Predicate %lld in the previous config, but expected it "
+ "to be there",
+ (long long)id);
+ return false;
+ }
+ const int oldIndex = oldConditionTrackerIt->second;
+ newConditionTrackers.push_back(oldConditionTrackers[oldIndex]);
+ break;
+ }
+ case UPDATE_REPLACE:
+ replacedConditions.insert(id);
+ [[fallthrough]]; // Intentionally fallthrough to create the new condition tracker.
+ case UPDATE_NEW: {
+ sp<ConditionTracker> tracker =
+ createConditionTracker(key, predicate, i, atomMatchingTrackerMap);
+ if (tracker == nullptr) {
+ return false;
+ }
+ newConditionTrackers.push_back(tracker);
+ break;
+ }
+ default: {
+ ALOGE("Condition \"%lld\" update state is unknown. This should never happen",
+ (long long)id);
+ return false;
+ }
+ }
+ }
+
+ // Update indices of preserved predicates.
+ for (const int conditionIndex : preservedConditions) {
+ if (!newConditionTrackers[conditionIndex]->onConfigUpdated(
+ conditionProtos, conditionIndex, newConditionTrackers, atomMatchingTrackerMap,
+ newConditionTrackerMap)) {
+ ALOGE("Failed to update condition %lld",
+ (long long)newConditionTrackers[conditionIndex]->getConditionId());
+ return false;
+ }
+ }
+
+ std::fill(cycleTracker.begin(), cycleTracker.end(), false);
+ for (int conditionIndex = 0; conditionIndex < conditionTrackerCount; conditionIndex++) {
+ const sp<ConditionTracker>& conditionTracker = newConditionTrackers[conditionIndex];
+ // Calling init on preserved conditions is OK. It is needed to fill the condition cache.
+ if (!conditionTracker->init(conditionProtos, newConditionTrackers, newConditionTrackerMap,
+ cycleTracker, conditionCache)) {
+ return false;
+ }
+ for (const int trackerIndex : conditionTracker->getAtomMatchingTrackerIndex()) {
+ vector<int>& conditionList = trackerToConditionMap[trackerIndex];
+ conditionList.push_back(conditionIndex);
+ }
+ }
+ return true;
+}
+
+bool updateStates(const StatsdConfig& config, const map<int64_t, uint64_t>& oldStateProtoHashes,
+ unordered_map<int64_t, int>& stateAtomIdMap,
+ unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+ map<int64_t, uint64_t>& newStateProtoHashes, set<int64_t>& replacedStates) {
+ // Share with metrics_manager_util.
+ if (!initStates(config, stateAtomIdMap, allStateGroupMaps, newStateProtoHashes)) {
+ return false;
+ }
+
+ for (const auto& [stateId, stateHash] : oldStateProtoHashes) {
+ const auto& it = newStateProtoHashes.find(stateId);
+ if (it != newStateProtoHashes.end() && it->second != stateHash) {
+ replacedStates.insert(stateId);
+ }
+ }
+ return true;
+}
+// Returns true if any matchers in the metric activation were replaced.
+bool metricActivationDepsChange(const StatsdConfig& config,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ const int64_t metricId, const set<int64_t>& replacedMatchers) {
+ const auto& metricActivationIt = metricToActivationMap.find(metricId);
+ if (metricActivationIt == metricToActivationMap.end()) {
+ return false;
+ }
+ const MetricActivation& metricActivation = config.metric_activation(metricActivationIt->second);
+ for (int i = 0; i < metricActivation.event_activation_size(); i++) {
+ const EventActivation& activation = metricActivation.event_activation(i);
+ if (replacedMatchers.find(activation.atom_matcher_id()) != replacedMatchers.end()) {
+ return true;
+ }
+ if (activation.has_deactivation_atom_matcher_id()) {
+ if (replacedMatchers.find(activation.deactivation_atom_matcher_id()) !=
+ replacedMatchers.end()) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool determineMetricUpdateStatus(
+ const StatsdConfig& config, const MessageLite& metric, const int64_t metricId,
+ const MetricType metricType, const set<int64_t>& matcherDependencies,
+ const set<int64_t>& conditionDependencies,
+ const ::google::protobuf::RepeatedField<int64_t>& stateDependencies,
+ const ::google::protobuf::RepeatedPtrField<MetricConditionLink>& conditionLinks,
+ const unordered_map<int64_t, int>& oldMetricProducerMap,
+ const vector<sp<MetricProducer>>& oldMetricProducers,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ const set<int64_t>& replacedMatchers, const set<int64_t>& replacedConditions,
+ const set<int64_t>& replacedStates, UpdateStatus& updateStatus) {
+ // Check if new metric
+ const auto& oldMetricProducerIt = oldMetricProducerMap.find(metricId);
+ if (oldMetricProducerIt == oldMetricProducerMap.end()) {
+ updateStatus = UPDATE_NEW;
+ return true;
+ }
+
+ // This is an existing metric, check if it has changed.
+ uint64_t metricHash;
+ if (!getMetricProtoHash(config, metric, metricId, metricToActivationMap, metricHash)) {
+ return false;
+ }
+ const sp<MetricProducer> oldMetricProducer = oldMetricProducers[oldMetricProducerIt->second];
+ if (oldMetricProducer->getMetricType() != metricType ||
+ oldMetricProducer->getProtoHash() != metricHash) {
+ updateStatus = UPDATE_REPLACE;
+ return true;
+ }
+
+ // Take intersections of the matchers/predicates/states that the metric
+ // depends on with those that have been replaced. If a metric depends on any
+ // replaced component, it too must be replaced.
+ set<int64_t> intersection;
+ set_intersection(matcherDependencies.begin(), matcherDependencies.end(),
+ replacedMatchers.begin(), replacedMatchers.end(),
+ inserter(intersection, intersection.begin()));
+ if (intersection.size() > 0) {
+ updateStatus = UPDATE_REPLACE;
+ return true;
+ }
+ set_intersection(conditionDependencies.begin(), conditionDependencies.end(),
+ replacedConditions.begin(), replacedConditions.end(),
+ inserter(intersection, intersection.begin()));
+ if (intersection.size() > 0) {
+ updateStatus = UPDATE_REPLACE;
+ return true;
+ }
+ set_intersection(stateDependencies.begin(), stateDependencies.end(), replacedStates.begin(),
+ replacedStates.end(), inserter(intersection, intersection.begin()));
+ if (intersection.size() > 0) {
+ updateStatus = UPDATE_REPLACE;
+ return true;
+ }
+
+ for (const auto& metricConditionLink : conditionLinks) {
+ if (replacedConditions.find(metricConditionLink.condition()) != replacedConditions.end()) {
+ updateStatus = UPDATE_REPLACE;
+ return true;
+ }
+ }
+
+ if (metricActivationDepsChange(config, metricToActivationMap, metricId, replacedMatchers)) {
+ updateStatus = UPDATE_REPLACE;
+ return true;
+ }
+
+ updateStatus = UPDATE_PRESERVE;
+ return true;
+}
+
+bool determineAllMetricUpdateStatuses(const StatsdConfig& config,
+ const unordered_map<int64_t, int>& oldMetricProducerMap,
+ const vector<sp<MetricProducer>>& oldMetricProducers,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ const set<int64_t>& replacedMatchers,
+ const set<int64_t>& replacedConditions,
+ const set<int64_t>& replacedStates,
+ vector<UpdateStatus>& metricsToUpdate) {
+ int metricIndex = 0;
+ for (int i = 0; i < config.count_metric_size(); i++, metricIndex++) {
+ const CountMetric& metric = config.count_metric(i);
+ set<int64_t> conditionDependencies;
+ if (metric.has_condition()) {
+ conditionDependencies.insert(metric.condition());
+ }
+ if (!determineMetricUpdateStatus(
+ config, metric, metric.id(), METRIC_TYPE_COUNT, {metric.what()},
+ conditionDependencies, metric.slice_by_state(), metric.links(),
+ oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ replacedMatchers, replacedConditions, replacedStates,
+ metricsToUpdate[metricIndex])) {
+ return false;
+ }
+ }
+ for (int i = 0; i < config.duration_metric_size(); i++, metricIndex++) {
+ const DurationMetric& metric = config.duration_metric(i);
+ set<int64_t> conditionDependencies({metric.what()});
+ if (metric.has_condition()) {
+ conditionDependencies.insert(metric.condition());
+ }
+ if (!determineMetricUpdateStatus(
+ config, metric, metric.id(), METRIC_TYPE_DURATION, /*matcherDependencies=*/{},
+ conditionDependencies, metric.slice_by_state(), metric.links(),
+ oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ replacedMatchers, replacedConditions, replacedStates,
+ metricsToUpdate[metricIndex])) {
+ return false;
+ }
+ }
+ for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) {
+ const EventMetric& metric = config.event_metric(i);
+ set<int64_t> conditionDependencies;
+ if (metric.has_condition()) {
+ conditionDependencies.insert(metric.condition());
+ }
+ if (!determineMetricUpdateStatus(
+ config, metric, metric.id(), METRIC_TYPE_EVENT, {metric.what()},
+ conditionDependencies, ::google::protobuf::RepeatedField<int64_t>(),
+ metric.links(), oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ replacedMatchers, replacedConditions, replacedStates,
+ metricsToUpdate[metricIndex])) {
+ return false;
+ }
+ }
+ for (int i = 0; i < config.value_metric_size(); i++, metricIndex++) {
+ const ValueMetric& metric = config.value_metric(i);
+ set<int64_t> conditionDependencies;
+ if (metric.has_condition()) {
+ conditionDependencies.insert(metric.condition());
+ }
+ if (!determineMetricUpdateStatus(
+ config, metric, metric.id(), METRIC_TYPE_VALUE, {metric.what()},
+ conditionDependencies, metric.slice_by_state(), metric.links(),
+ oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ replacedMatchers, replacedConditions, replacedStates,
+ metricsToUpdate[metricIndex])) {
+ return false;
+ }
+ }
+ for (int i = 0; i < config.gauge_metric_size(); i++, metricIndex++) {
+ const GaugeMetric& metric = config.gauge_metric(i);
+ set<int64_t> conditionDependencies;
+ if (metric.has_condition()) {
+ conditionDependencies.insert(metric.condition());
+ }
+ set<int64_t> matcherDependencies({metric.what()});
+ if (metric.has_trigger_event()) {
+ matcherDependencies.insert(metric.trigger_event());
+ }
+ if (!determineMetricUpdateStatus(
+ config, metric, metric.id(), METRIC_TYPE_GAUGE, matcherDependencies,
+ conditionDependencies, ::google::protobuf::RepeatedField<int64_t>(),
+ metric.links(), oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ replacedMatchers, replacedConditions, replacedStates,
+ metricsToUpdate[metricIndex])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// Called when a metric is preserved during a config update. Finds the metric in oldMetricProducers
+// and calls onConfigUpdated to update all indices.
+optional<sp<MetricProducer>> updateMetric(
+ const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const int64_t metricId, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+ const unordered_map<int64_t, int>& oldMetricProducerMap,
+ const vector<sp<MetricProducer>>& oldMetricProducers,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
+ const auto& oldMetricProducerIt = oldMetricProducerMap.find(metricId);
+ if (oldMetricProducerIt == oldMetricProducerMap.end()) {
+ ALOGE("Could not find Metric %lld in the previous config, but expected it "
+ "to be there",
+ (long long)metricId);
+ return nullopt;
+ }
+ const int oldIndex = oldMetricProducerIt->second;
+ sp<MetricProducer> producer = oldMetricProducers[oldIndex];
+ if (!producer->onConfigUpdated(config, configIndex, metricIndex, allAtomMatchingTrackers,
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap,
+ matcherWizard, allConditionTrackers, conditionTrackerMap, wizard,
+ metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
+ activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+ return nullopt;
+ }
+ return {producer};
+}
+
+bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+ const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const set<int64_t>& replacedMatchers,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& conditionTrackerMap,
+ const set<int64_t>& replacedConditions,
+ vector<sp<ConditionTracker>>& allConditionTrackers,
+ const vector<ConditionState>& initialConditionCache,
+ const unordered_map<int64_t, int>& stateAtomIdMap,
+ const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+ const set<int64_t>& replacedStates,
+ const unordered_map<int64_t, int>& oldMetricProducerMap,
+ const vector<sp<MetricProducer>>& oldMetricProducers,
+ unordered_map<int64_t, int>& newMetricProducerMap,
+ vector<sp<MetricProducer>>& newMetricProducers,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ set<int64_t>& noReportMetricIds,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation, set<int64_t>& replacedMetrics) {
+ sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
+ sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchingTrackers);
+ const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
+ config.event_metric_size() + config.gauge_metric_size() +
+ config.value_metric_size();
+ newMetricProducers.reserve(allMetricsCount);
+
+ // Construct map from metric id to metric activation index. The map will be used to determine
+ // the metric activation corresponding to a metric.
+ unordered_map<int64_t, int> metricToActivationMap;
+ for (int i = 0; i < config.metric_activation_size(); i++) {
+ const MetricActivation& metricActivation = config.metric_activation(i);
+ int64_t metricId = metricActivation.metric_id();
+ if (metricToActivationMap.find(metricId) != metricToActivationMap.end()) {
+ ALOGE("Metric %lld has multiple MetricActivations", (long long)metricId);
+ return false;
+ }
+ metricToActivationMap.insert({metricId, i});
+ }
+
+ vector<UpdateStatus> metricsToUpdate(allMetricsCount, UPDATE_UNKNOWN);
+ if (!determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap, replacedMatchers,
+ replacedConditions, replacedStates, metricsToUpdate)) {
+ return false;
+ }
+
+ // Now, perform the update. Must iterate the metric types in the same order
+ int metricIndex = 0;
+ for (int i = 0; i < config.count_metric_size(); i++, metricIndex++) {
+ const CountMetric& metric = config.count_metric(i);
+ newMetricProducerMap[metric.id()] = metricIndex;
+ optional<sp<MetricProducer>> producer;
+ switch (metricsToUpdate[metricIndex]) {
+ case UPDATE_PRESERVE: {
+ producer = updateMetric(
+ config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+ allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
+ oldMetricProducers, metricToActivationMap, trackerToMetricMap,
+ conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation);
+ break;
+ }
+ case UPDATE_REPLACE:
+ replacedMetrics.insert(metric.id());
+ [[fallthrough]]; // Intentionally fallthrough to create the new metric producer.
+ case UPDATE_NEW: {
+ producer = createCountMetricProducerAndUpdateMetadata(
+ key, config, timeBaseNs, currentTimeNs, metric, metricIndex,
+ allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers,
+ conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
+ allStateGroupMaps, metricToActivationMap, trackerToMetricMap,
+ conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation);
+ break;
+ }
+ default: {
+ ALOGE("Metric \"%lld\" update state is unknown. This should never happen",
+ (long long)metric.id());
+ return false;
+ }
+ }
+ if (!producer) {
+ return false;
+ }
+ newMetricProducers.push_back(producer.value());
+ }
+ for (int i = 0; i < config.duration_metric_size(); i++, metricIndex++) {
+ const DurationMetric& metric = config.duration_metric(i);
+ newMetricProducerMap[metric.id()] = metricIndex;
+ optional<sp<MetricProducer>> producer;
+ switch (metricsToUpdate[metricIndex]) {
+ case UPDATE_PRESERVE: {
+ producer = updateMetric(
+ config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+ allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
+ oldMetricProducers, metricToActivationMap, trackerToMetricMap,
+ conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation);
+ break;
+ }
+ case UPDATE_REPLACE:
+ replacedMetrics.insert(metric.id());
+ [[fallthrough]]; // Intentionally fallthrough to create the new metric producer.
+ case UPDATE_NEW: {
+ producer = createDurationMetricProducerAndUpdateMetadata(
+ key, config, timeBaseNs, currentTimeNs, metric, metricIndex,
+ allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers,
+ conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
+ allStateGroupMaps, metricToActivationMap, trackerToMetricMap,
+ conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation);
+ break;
+ }
+ default: {
+ ALOGE("Metric \"%lld\" update state is unknown. This should never happen",
+ (long long)metric.id());
+ return false;
+ }
+ }
+ if (!producer) {
+ return false;
+ }
+ newMetricProducers.push_back(producer.value());
+ }
+ for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) {
+ const EventMetric& metric = config.event_metric(i);
+ newMetricProducerMap[metric.id()] = metricIndex;
+ optional<sp<MetricProducer>> producer;
+ switch (metricsToUpdate[metricIndex]) {
+ case UPDATE_PRESERVE: {
+ producer = updateMetric(
+ config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+ allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
+ oldMetricProducers, metricToActivationMap, trackerToMetricMap,
+ conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation);
+ break;
+ }
+ case UPDATE_REPLACE:
+ replacedMetrics.insert(metric.id());
+ [[fallthrough]]; // Intentionally fallthrough to create the new metric producer.
+ case UPDATE_NEW: {
+ producer = createEventMetricProducerAndUpdateMetadata(
+ key, config, timeBaseNs, metric, metricIndex, allAtomMatchingTrackers,
+ newAtomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap,
+ initialConditionCache, wizard, metricToActivationMap, trackerToMetricMap,
+ conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation);
+ break;
+ }
+ default: {
+ ALOGE("Metric \"%lld\" update state is unknown. This should never happen",
+ (long long)metric.id());
+ return false;
+ }
+ }
+ if (!producer) {
+ return false;
+ }
+ newMetricProducers.push_back(producer.value());
+ }
+
+ for (int i = 0; i < config.value_metric_size(); i++, metricIndex++) {
+ const ValueMetric& metric = config.value_metric(i);
+ newMetricProducerMap[metric.id()] = metricIndex;
+ optional<sp<MetricProducer>> producer;
+ switch (metricsToUpdate[metricIndex]) {
+ case UPDATE_PRESERVE: {
+ producer = updateMetric(
+ config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+ allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
+ oldMetricProducers, metricToActivationMap, trackerToMetricMap,
+ conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation);
+ break;
+ }
+ case UPDATE_REPLACE:
+ replacedMetrics.insert(metric.id());
+ [[fallthrough]]; // Intentionally fallthrough to create the new metric producer.
+ case UPDATE_NEW: {
+ producer = createValueMetricProducerAndUpdateMetadata(
+ key, config, timeBaseNs, currentTimeNs, pullerManager, metric, metricIndex,
+ allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers,
+ conditionTrackerMap, initialConditionCache, wizard, matcherWizard,
+ stateAtomIdMap, allStateGroupMaps, metricToActivationMap,
+ trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation);
+ break;
+ }
+ default: {
+ ALOGE("Metric \"%lld\" update state is unknown. This should never happen",
+ (long long)metric.id());
+ return false;
+ }
+ }
+ if (!producer) {
+ return false;
+ }
+ newMetricProducers.push_back(producer.value());
+ }
+
+ for (int i = 0; i < config.gauge_metric_size(); i++, metricIndex++) {
+ const GaugeMetric& metric = config.gauge_metric(i);
+ newMetricProducerMap[metric.id()] = metricIndex;
+ optional<sp<MetricProducer>> producer;
+ switch (metricsToUpdate[metricIndex]) {
+ case UPDATE_PRESERVE: {
+ producer = updateMetric(
+ config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+ allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
+ oldMetricProducers, metricToActivationMap, trackerToMetricMap,
+ conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation);
+ break;
+ }
+ case UPDATE_REPLACE:
+ replacedMetrics.insert(metric.id());
+ [[fallthrough]]; // Intentionally fallthrough to create the new metric producer.
+ case UPDATE_NEW: {
+ producer = createGaugeMetricProducerAndUpdateMetadata(
+ key, config, timeBaseNs, currentTimeNs, pullerManager, metric, metricIndex,
+ allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers,
+ conditionTrackerMap, initialConditionCache, wizard, matcherWizard,
+ metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation);
+ break;
+ }
+ default: {
+ ALOGE("Metric \"%lld\" update state is unknown. This should never happen",
+ (long long)metric.id());
+ return false;
+ }
+ }
+ if (!producer) {
+ return false;
+ }
+ newMetricProducers.push_back(producer.value());
+ }
+
+ for (int i = 0; i < config.no_report_metric_size(); ++i) {
+ const int64_t noReportMetric = config.no_report_metric(i);
+ if (newMetricProducerMap.find(noReportMetric) == newMetricProducerMap.end()) {
+ ALOGW("no_report_metric %" PRId64 " not exist", noReportMetric);
+ return false;
+ }
+ noReportMetricIds.insert(noReportMetric);
+ }
+ const set<int> atomsAllowedFromAnyUid(config.whitelisted_atom_ids().begin(),
+ config.whitelisted_atom_ids().end());
+ for (int i = 0; i < allMetricsCount; i++) {
+ sp<MetricProducer> producer = newMetricProducers[i];
+ // Register metrics to StateTrackers
+ for (int atomId : producer->getSlicedStateAtoms()) {
+ // Register listener for atoms that use allowed_log_sources.
+ // Using atoms allowed from any uid as a sliced state atom is not allowed.
+ // Redo this check for all metrics in case the atoms allowed from any uid changed.
+ if (atomsAllowedFromAnyUid.find(atomId) != atomsAllowedFromAnyUid.end()) {
+ return false;
+ // Preserved metrics should've already registered.`
+ } else if (metricsToUpdate[i] != UPDATE_PRESERVE) {
+ StateManager::getInstance().registerListener(atomId, producer);
+ }
+ }
+ }
+
+ // Init new/replaced metrics.
+ for (size_t i = 0; i < newMetricProducers.size(); i++) {
+ if (metricsToUpdate[i] == UPDATE_REPLACE || metricsToUpdate[i] == UPDATE_NEW) {
+ newMetricProducers[i]->prepareFirstBucket();
+ }
+ }
+ return true;
+}
+
+bool determineAlertUpdateStatus(const Alert& alert,
+ const unordered_map<int64_t, int>& oldAlertTrackerMap,
+ const vector<sp<AnomalyTracker>>& oldAnomalyTrackers,
+ const set<int64_t>& replacedMetrics, UpdateStatus& updateStatus) {
+ // Check if new alert.
+ const auto& oldAnomalyTrackerIt = oldAlertTrackerMap.find(alert.id());
+ if (oldAnomalyTrackerIt == oldAlertTrackerMap.end()) {
+ updateStatus = UPDATE_NEW;
+ return true;
+ }
+
+ // This is an existing alert, check if it has changed.
+ string serializedAlert;
+ if (!alert.SerializeToString(&serializedAlert)) {
+ ALOGW("Unable to serialize alert %lld", (long long)alert.id());
+ return false;
+ }
+ uint64_t newProtoHash = Hash64(serializedAlert);
+ const auto [success, oldProtoHash] =
+ oldAnomalyTrackers[oldAnomalyTrackerIt->second]->getProtoHash();
+ if (!success) {
+ return false;
+ }
+ if (newProtoHash != oldProtoHash) {
+ updateStatus = UPDATE_REPLACE;
+ return true;
+ }
+
+ // Check if the metric this alert relies on has changed.
+ if (replacedMetrics.find(alert.metric_id()) != replacedMetrics.end()) {
+ updateStatus = UPDATE_REPLACE;
+ return true;
+ }
+
+ updateStatus = UPDATE_PRESERVE;
+ return true;
+}
+
+bool updateAlerts(const StatsdConfig& config, const unordered_map<int64_t, int>& metricProducerMap,
+ const set<int64_t>& replacedMetrics,
+ const unordered_map<int64_t, int>& oldAlertTrackerMap,
+ const vector<sp<AnomalyTracker>>& oldAnomalyTrackers,
+ const sp<AlarmMonitor>& anomalyAlarmMonitor,
+ vector<sp<MetricProducer>>& allMetricProducers,
+ unordered_map<int64_t, int>& newAlertTrackerMap,
+ vector<sp<AnomalyTracker>>& newAnomalyTrackers) {
+ int alertCount = config.alert_size();
+ vector<UpdateStatus> alertUpdateStatuses(alertCount);
+ for (int i = 0; i < alertCount; i++) {
+ if (!determineAlertUpdateStatus(config.alert(i), oldAlertTrackerMap, oldAnomalyTrackers,
+ replacedMetrics, alertUpdateStatuses[i])) {
+ return false;
+ }
+ }
+
+ for (int i = 0; i < alertCount; i++) {
+ const Alert& alert = config.alert(i);
+ newAlertTrackerMap[alert.id()] = newAnomalyTrackers.size();
+ switch (alertUpdateStatuses[i]) {
+ case UPDATE_PRESERVE: {
+ // Find the alert and update it.
+ const auto& oldAnomalyTrackerIt = oldAlertTrackerMap.find(alert.id());
+ if (oldAnomalyTrackerIt == oldAlertTrackerMap.end()) {
+ ALOGW("Could not find AnomalyTracker %lld in the previous config, but "
+ "expected it to be there",
+ (long long)alert.id());
+ return false;
+ }
+ sp<AnomalyTracker> anomalyTracker = oldAnomalyTrackers[oldAnomalyTrackerIt->second];
+ anomalyTracker->onConfigUpdated();
+ // Add the alert to the relevant metric.
+ const auto& metricProducerIt = metricProducerMap.find(alert.metric_id());
+ if (metricProducerIt == metricProducerMap.end()) {
+ ALOGW("alert \"%lld\" has unknown metric id: \"%lld\"", (long long)alert.id(),
+ (long long)alert.metric_id());
+ return false;
+ }
+ allMetricProducers[metricProducerIt->second]->addAnomalyTracker(anomalyTracker);
+ newAnomalyTrackers.push_back(anomalyTracker);
+ break;
+ }
+ case UPDATE_REPLACE:
+ case UPDATE_NEW: {
+ optional<sp<AnomalyTracker>> anomalyTracker = createAnomalyTracker(
+ alert, anomalyAlarmMonitor, metricProducerMap, allMetricProducers);
+ if (!anomalyTracker) {
+ return false;
+ }
+ newAnomalyTrackers.push_back(anomalyTracker.value());
+ break;
+ }
+ default: {
+ ALOGE("Alert \"%lld\" update state is unknown. This should never happen",
+ (long long)alert.id());
+ return false;
+ }
+ }
+ }
+ if (!initSubscribersForSubscriptionType(config, Subscription::ALERT, newAlertTrackerMap,
+ newAnomalyTrackers)) {
+ return false;
+ }
+ return true;
+}
+
+bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,
+ const sp<StatsPullerManager>& pullerManager,
+ const sp<AlarmMonitor>& anomalyAlarmMonitor,
+ const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
+ const int64_t currentTimeNs,
+ const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const vector<sp<ConditionTracker>>& oldConditionTrackers,
+ const unordered_map<int64_t, int>& oldConditionTrackerMap,
+ const vector<sp<MetricProducer>>& oldMetricProducers,
+ const unordered_map<int64_t, int>& oldMetricProducerMap,
+ const vector<sp<AnomalyTracker>>& oldAnomalyTrackers,
+ const unordered_map<int64_t, int>& oldAlertTrackerMap,
+ const map<int64_t, uint64_t>& oldStateProtoHashes, set<int>& allTagIds,
+ vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
+ unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ vector<sp<ConditionTracker>>& newConditionTrackers,
+ unordered_map<int64_t, int>& newConditionTrackerMap,
+ vector<sp<MetricProducer>>& newMetricProducers,
+ unordered_map<int64_t, int>& newMetricProducerMap,
+ vector<sp<AnomalyTracker>>& newAnomalyTrackers,
+ unordered_map<int64_t, int>& newAlertTrackerMap,
+ vector<sp<AlarmTracker>>& newPeriodicAlarmTrackers,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int, vector<int>>& trackerToConditionMap,
+ unordered_map<int, vector<int>>& activationTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationTrackerToMetricMap,
+ vector<int>& metricsWithActivation,
+ map<int64_t, uint64_t>& newStateProtoHashes,
+ set<int64_t>& noReportMetricIds) {
+ set<int64_t> replacedMatchers;
+ set<int64_t> replacedConditions;
+ set<int64_t> replacedStates;
+ set<int64_t> replacedMetrics;
+ vector<ConditionState> conditionCache;
+ unordered_map<int64_t, int> stateAtomIdMap;
+ unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
+
+ if (!updateAtomMatchingTrackers(config, uidMap, oldAtomMatchingTrackerMap,
+ oldAtomMatchingTrackers, allTagIds, newAtomMatchingTrackerMap,
+ newAtomMatchingTrackers, replacedMatchers)) {
+ ALOGE("updateAtomMatchingTrackers failed");
+ return false;
+ }
+
+ if (!updateConditions(key, config, newAtomMatchingTrackerMap, replacedMatchers,
+ oldConditionTrackerMap, oldConditionTrackers, newConditionTrackerMap,
+ newConditionTrackers, trackerToConditionMap, conditionCache,
+ replacedConditions)) {
+ ALOGE("updateConditions failed");
+ return false;
+ }
+
+ if (!updateStates(config, oldStateProtoHashes, stateAtomIdMap, allStateGroupMaps,
+ newStateProtoHashes, replacedStates)) {
+ ALOGE("updateStates failed");
+ return false;
+ }
+ if (!updateMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager,
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
+ newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions,
+ newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps,
+ replacedStates, oldMetricProducerMap, oldMetricProducers,
+ newMetricProducerMap, newMetricProducers, conditionToMetricMap,
+ trackerToMetricMap, noReportMetricIds, activationTrackerToMetricMap,
+ deactivationTrackerToMetricMap, metricsWithActivation, replacedMetrics)) {
+ ALOGE("updateMetrics failed");
+ return false;
+ }
+
+ if (!updateAlerts(config, newMetricProducerMap, replacedMetrics, oldAlertTrackerMap,
+ oldAnomalyTrackers, anomalyAlarmMonitor, newMetricProducers,
+ newAlertTrackerMap, newAnomalyTrackers)) {
+ ALOGE("updateAlerts failed");
+ return false;
+ }
+
+ // Alarms do not have any state, so we can reuse the initialization logic.
+ if (!initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs,
+ newPeriodicAlarmTrackers)) {
+ ALOGE("initAlarms failed");
+ return false;
+ }
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/bin/src/metrics/parsing_utils/config_update_utils.h b/bin/src/metrics/parsing_utils/config_update_utils.h
new file mode 100644
index 0000000..8e2be68
--- /dev/null
+++ b/bin/src/metrics/parsing_utils/config_update_utils.h
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include "anomaly/AlarmMonitor.h"
+#include "anomaly/AlarmTracker.h"
+#include "condition/ConditionTracker.h"
+#include "external/StatsPullerManager.h"
+#include "matchers/AtomMatchingTracker.h"
+#include "metrics/MetricProducer.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Helper functions for MetricsManager to update itself from a new StatsdConfig.
+// *Note*: only updateStatsdConfig() should be called from outside this file.
+// All other functions are intermediate steps, created to make unit testing easier.
+
+// Possible update states for a component. PRESERVE means we should keep the existing one.
+// REPLACE means we should create a new one because the existing one changed
+// NEW means we should create a new one because one does not currently exist.
+enum UpdateStatus {
+ UPDATE_UNKNOWN = 0,
+ UPDATE_PRESERVE = 1,
+ UPDATE_REPLACE = 2,
+ UPDATE_NEW = 3,
+};
+
+// Recursive function to determine if a matcher needs to be updated.
+// input:
+// [config]: the input StatsdConfig
+// [matcherIdx]: the index of the current matcher to be updated
+// [oldAtomMatchingTrackerMap]: matcher id to index mapping in the existing MetricsManager
+// [oldAtomMatchingTrackers]: stores the existing AtomMatchingTrackers
+// [newAtomMatchingTrackerMap]: matcher id to index mapping in the input StatsdConfig
+// output:
+// [matchersToUpdate]: vector of the update status of each matcher. The matcherIdx index will
+// be updated from UPDATE_UNKNOWN after this call.
+// [cycleTracker]: intermediate param used during recursion.
+// Returns whether the function was successful or not.
+bool determineMatcherUpdateStatus(
+ const StatsdConfig& config, const int matcherIdx,
+ const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ std::vector<UpdateStatus>& matchersToUpdate, std::vector<bool>& cycleTracker);
+
+// Updates the AtomMatchingTrackers.
+// input:
+// [config]: the input StatsdConfig
+// [oldAtomMatchingTrackerMap]: existing matcher id to index mapping
+// [oldAtomMatchingTrackers]: stores the existing AtomMatchingTrackers
+// output:
+// [allTagIds]: contains the set of all interesting tag ids to this config.
+// [newAtomMatchingTrackerMap]: new matcher id to index mapping
+// [newAtomMatchingTrackers]: stores the new AtomMatchingTrackers
+// [replacedMatchers]: set of matcher ids that changed and have been replaced
+bool updateAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
+ const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
+ std::set<int>& allTagIds,
+ std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ std::vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
+ std::set<int64_t>& replacedMatchers);
+
+// Recursive function to determine if a condition needs to be updated.
+// input:
+// [config]: the input StatsdConfig
+// [conditionIdx]: the index of the current condition to be updated
+// [oldConditionTrackerMap]: condition id to index mapping in the existing MetricsManager
+// [oldConditionTrackers]: stores the existing ConditionTrackers
+// [newConditionTrackerMap]: condition id to index mapping in the input StatsdConfig
+// [replacedMatchers]: set of replaced matcher ids. conditions using these matchers must be replaced
+// output:
+// [conditionsToUpdate]: vector of the update status of each condition. The conditionIdx index will
+// be updated from UPDATE_UNKNOWN after this call.
+// [cycleTracker]: intermediate param used during recursion.
+// Returns whether the function was successful or not.
+bool determineConditionUpdateStatus(const StatsdConfig& config, const int conditionIdx,
+ const std::unordered_map<int64_t, int>& oldConditionTrackerMap,
+ const std::vector<sp<ConditionTracker>>& oldConditionTrackers,
+ const std::unordered_map<int64_t, int>& newConditionTrackerMap,
+ const std::set<int64_t>& replacedMatchers,
+ std::vector<UpdateStatus>& conditionsToUpdate,
+ std::vector<bool>& cycleTracker);
+
+// Updates ConditionTrackers
+// input:
+// [config]: the input config
+// [atomMatchingTrackerMap]: AtomMatchingTracker name to index mapping from previous step.
+// [replacedMatchers]: ids of replaced matchers. conditions depending on these must also be replaced
+// [oldConditionTrackerMap]: existing matcher id to index mapping
+// [oldConditionTrackers]: stores the existing ConditionTrackers
+// output:
+// [newConditionTrackerMap]: new condition id to index mapping
+// [newConditionTrackers]: stores the sp to all the ConditionTrackers
+// [trackerToConditionMap]: contains the mapping from the index of an atom matcher
+// to indices of condition trackers that use the matcher
+// [conditionCache]: stores the current conditions for each ConditionTracker
+// [replacedConditions]: set of condition ids that have changed and have been replaced
+bool updateConditions(const ConfigKey& key, const StatsdConfig& config,
+ const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ const std::set<int64_t>& replacedMatchers,
+ const std::unordered_map<int64_t, int>& oldConditionTrackerMap,
+ const std::vector<sp<ConditionTracker>>& oldConditionTrackers,
+ std::unordered_map<int64_t, int>& newConditionTrackerMap,
+ std::vector<sp<ConditionTracker>>& newConditionTrackers,
+ std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
+ std::vector<ConditionState>& conditionCache,
+ std::set<int64_t>& replacedConditions);
+
+bool updateStates(const StatsdConfig& config,
+ const std::map<int64_t, uint64_t>& oldStateProtoHashes,
+ std::unordered_map<int64_t, int>& stateAtomIdMap,
+ std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps,
+ std::map<int64_t, uint64_t>& newStateProtoHashes,
+ std::set<int64_t>& replacedStates);
+
+// Function to determine the update status (preserve/replace/new) of all metrics in the config.
+// [config]: the input StatsdConfig
+// [oldMetricProducerMap]: metric id to index mapping in the existing MetricsManager
+// [oldMetricProducers]: stores the existing MetricProducers
+// [metricToActivationMap]: map from metric id to metric activation index
+// [replacedMatchers]: set of replaced matcher ids. metrics using these matchers must be replaced
+// [replacedConditions]: set of replaced conditions. metrics using these conditions must be replaced
+// [replacedStates]: set of replaced state ids. metrics using these states must be replaced
+// output:
+// [metricsToUpdate]: update status of each metric. Will be changed from UPDATE_UNKNOWN
+// Returns whether the function was successful or not.
+bool determineAllMetricUpdateStatuses(const StatsdConfig& config,
+ const unordered_map<int64_t, int>& oldMetricProducerMap,
+ const vector<sp<MetricProducer>>& oldMetricProducers,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ const set<int64_t>& replacedMatchers,
+ const set<int64_t>& replacedConditions,
+ const set<int64_t>& replacedStates,
+ vector<UpdateStatus>& metricsToUpdate);
+
+// Update MetricProducers.
+// input:
+// [key]: the config key that this config belongs to
+// [config]: the input config
+// [timeBaseNs]: start time base for all metrics
+// [currentTimeNs]: time of the config update
+// [atomMatchingTrackerMap]: AtomMatchingTracker name to index mapping from previous step.
+// [replacedMatchers]: ids of replaced matchers. Metrics depending on these must also be replaced
+// [allAtomMatchingTrackers]: stores the sp of the atom matchers.
+// [conditionTrackerMap]: condition name to index mapping
+// [replacedConditions]: set of condition ids that have changed and have been replaced
+// [stateAtomIdMap]: contains the mapping from state ids to atom ids
+// [allStateGroupMaps]: contains the mapping from atom ids and state values to
+// state group ids for all states
+// output:
+// [allMetricProducers]: contains the list of sp to the MetricProducers created.
+// [conditionToMetricMap]: contains the mapping from condition tracker index to
+// the list of MetricProducer index
+// [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
+bool updateMetrics(
+ const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+ const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const std::set<int64_t>& replacedMatchers,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const std::set<int64_t>& replacedConditions,
+ std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::vector<ConditionState>& initialConditionCache,
+ const std::unordered_map<int64_t, int>& stateAtomIdMap,
+ const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps,
+ const std::set<int64_t>& replacedStates,
+ const std::unordered_map<int64_t, int>& oldMetricProducerMap,
+ const std::vector<sp<MetricProducer>>& oldMetricProducers,
+ std::unordered_map<int64_t, int>& newMetricProducerMap,
+ std::vector<sp<MetricProducer>>& newMetricProducers,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::set<int64_t>& noReportMetricIds,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation, std::set<int64_t>& replacedMetrics);
+
+// Function to determine the update status (preserve/replace/new) of an alert.
+// [alert]: the input Alert
+// [oldAlertTrackerMap]: alert id to index mapping in the existing MetricsManager
+// [oldAnomalyTrackers]: stores the existing AnomalyTrackers
+// [replacedMetrics]: set of replaced metric ids. alerts using these metrics must be replaced
+// output:
+// [updateStatus]: update status of the alert. Will be changed from UPDATE_UNKNOWN
+// Returns whether the function was successful or not.
+bool determineAlertUpdateStatus(const Alert& alert,
+ const std::unordered_map<int64_t, int>& oldAlertTrackerMap,
+ const std::vector<sp<AnomalyTracker>>& oldAnomalyTrackers,
+ const std::set<int64_t>& replacedMetrics,
+ UpdateStatus& updateStatus);
+
+// Update MetricProducers.
+// input:
+// [config]: the input config
+// [metricProducerMap]: metric id to index mapping in the new config
+// [replacedMetrics]: set of metric ids that have changed and were replaced
+// [oldAlertTrackerMap]: alert id to index mapping in the existing MetricsManager.
+// [oldAnomalyTrackers]: stores the existing AnomalyTrackers
+// [anomalyAlarmMonitor]: AlarmMonitor used for duration metric anomaly detection
+// [allMetricProducers]: stores the sp of the metric producers, AnomalyTrackers need to be added.
+// [stateAtomIdMap]: contains the mapping from state ids to atom ids
+// [allStateGroupMaps]: contains the mapping from atom ids and state values to
+// state group ids for all states
+// output:
+// [newAlertTrackerMap]: mapping of alert id to index in the new config
+// [newAnomalyTrackers]: contains the list of sp to the AnomalyTrackers created.
+bool updateAlerts(const StatsdConfig& config,
+ const std::unordered_map<int64_t, int>& metricProducerMap,
+ const std::set<int64_t>& replacedMetrics,
+ const std::unordered_map<int64_t, int>& oldAlertTrackerMap,
+ const std::vector<sp<AnomalyTracker>>& oldAnomalyTrackers,
+ const sp<AlarmMonitor>& anomalyAlarmMonitor,
+ std::vector<sp<MetricProducer>>& allMetricProducers,
+ std::unordered_map<int64_t, int>& newAlertTrackerMap,
+ std::vector<sp<AnomalyTracker>>& newAnomalyTrackers);
+
+// Updates the existing MetricsManager from a new StatsdConfig.
+// Parameters are the members of MetricsManager. See MetricsManager for declaration.
+bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,
+ const sp<StatsPullerManager>& pullerManager,
+ const sp<AlarmMonitor>& anomalyAlarmMonitor,
+ const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
+ const int64_t currentTimeNs,
+ const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const std::vector<sp<ConditionTracker>>& oldConditionTrackers,
+ const std::unordered_map<int64_t, int>& oldConditionTrackerMap,
+ const std::vector<sp<MetricProducer>>& oldMetricProducers,
+ const std::unordered_map<int64_t, int>& oldMetricProducerMap,
+ const std::vector<sp<AnomalyTracker>>& oldAnomalyTrackers,
+ const std::unordered_map<int64_t, int>& oldAlertTrackerMap,
+ const std::map<int64_t, uint64_t>& oldStateProtoHashes,
+ std::set<int>& allTagIds,
+ std::vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
+ std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ std::vector<sp<ConditionTracker>>& newConditionTrackers,
+ std::unordered_map<int64_t, int>& newConditionTrackerMap,
+ std::vector<sp<MetricProducer>>& newMetricProducers,
+ std::unordered_map<int64_t, int>& newMetricProducerMap,
+ std::vector<sp<AnomalyTracker>>& newAlertTrackers,
+ std::unordered_map<int64_t, int>& newAlertTrackerMap,
+ std::vector<sp<AlarmTracker>>& newPeriodicAlarmTrackers,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
+ std::unordered_map<int, std::vector<int>>& activationTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation,
+ std::map<int64_t, uint64_t>& newStateProtoHashes,
+ std::set<int64_t>& noReportMetricIds);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/bin/src/metrics/parsing_utils/metrics_manager_util.cpp b/bin/src/metrics/parsing_utils/metrics_manager_util.cpp
new file mode 100644
index 0000000..bfa409c
--- /dev/null
+++ b/bin/src/metrics/parsing_utils/metrics_manager_util.cpp
@@ -0,0 +1,1228 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define DEBUG false // STOPSHIP if true
+#include "Log.h"
+
+#include "metrics_manager_util.h"
+
+#include <inttypes.h>
+
+#include "FieldValue.h"
+#include "condition/CombinationConditionTracker.h"
+#include "condition/SimpleConditionTracker.h"
+#include "external/StatsPullerManager.h"
+#include "hash.h"
+#include "matchers/CombinationAtomMatchingTracker.h"
+#include "matchers/EventMatcherWizard.h"
+#include "matchers/SimpleAtomMatchingTracker.h"
+#include "metrics/CountMetricProducer.h"
+#include "metrics/DurationMetricProducer.h"
+#include "metrics/EventMetricProducer.h"
+#include "metrics/GaugeMetricProducer.h"
+#include "metrics/MetricProducer.h"
+#include "metrics/ValueMetricProducer.h"
+#include "state/StateManager.h"
+#include "stats_util.h"
+
+using google::protobuf::MessageLite;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+namespace {
+
+bool hasLeafNode(const FieldMatcher& matcher) {
+ if (!matcher.has_field()) {
+ return false;
+ }
+ for (int i = 0; i < matcher.child_size(); ++i) {
+ if (hasLeafNode(matcher.child(i))) {
+ return true;
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+sp<AtomMatchingTracker> createAtomMatchingTracker(const AtomMatcher& logMatcher, const int index,
+ const sp<UidMap>& uidMap) {
+ string serializedMatcher;
+ if (!logMatcher.SerializeToString(&serializedMatcher)) {
+ ALOGE("Unable to serialize matcher %lld", (long long)logMatcher.id());
+ return nullptr;
+ }
+ uint64_t protoHash = Hash64(serializedMatcher);
+ switch (logMatcher.contents_case()) {
+ case AtomMatcher::ContentsCase::kSimpleAtomMatcher:
+ return new SimpleAtomMatchingTracker(logMatcher.id(), index, protoHash,
+ logMatcher.simple_atom_matcher(), uidMap);
+ case AtomMatcher::ContentsCase::kCombination:
+ return new CombinationAtomMatchingTracker(logMatcher.id(), index, protoHash);
+ default:
+ ALOGE("Matcher \"%lld\" malformed", (long long)logMatcher.id());
+ return nullptr;
+ }
+}
+
+sp<ConditionTracker> createConditionTracker(
+ const ConfigKey& key, const Predicate& predicate, const int index,
+ const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
+ string serializedPredicate;
+ if (!predicate.SerializeToString(&serializedPredicate)) {
+ ALOGE("Unable to serialize predicate %lld", (long long)predicate.id());
+ return nullptr;
+ }
+ uint64_t protoHash = Hash64(serializedPredicate);
+ switch (predicate.contents_case()) {
+ case Predicate::ContentsCase::kSimplePredicate: {
+ return new SimpleConditionTracker(key, predicate.id(), protoHash, index,
+ predicate.simple_predicate(), atomMatchingTrackerMap);
+ }
+ case Predicate::ContentsCase::kCombination: {
+ return new CombinationConditionTracker(predicate.id(), index, protoHash);
+ }
+ default:
+ ALOGE("Predicate \"%lld\" malformed", (long long)predicate.id());
+ return nullptr;
+ }
+}
+
+bool getMetricProtoHash(const StatsdConfig& config, const MessageLite& metric, const int64_t id,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ uint64_t& metricHash) {
+ string serializedMetric;
+ if (!metric.SerializeToString(&serializedMetric)) {
+ ALOGE("Unable to serialize metric %lld", (long long)id);
+ return false;
+ }
+ metricHash = Hash64(serializedMetric);
+
+ // Combine with activation hash, if applicable
+ const auto& metricActivationIt = metricToActivationMap.find(id);
+ if (metricActivationIt != metricToActivationMap.end()) {
+ string serializedActivation;
+ const MetricActivation& activation = config.metric_activation(metricActivationIt->second);
+ if (!activation.SerializeToString(&serializedActivation)) {
+ ALOGE("Unable to serialize metric activation for metric %lld", (long long)id);
+ return false;
+ }
+ metricHash = Hash64(to_string(metricHash).append(to_string(Hash64(serializedActivation))));
+ }
+ return true;
+}
+
+bool handleMetricWithAtomMatchingTrackers(
+ const int64_t matcherId, const int metricIndex, const bool enforceOneAtom,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap, int& logTrackerIndex) {
+ auto logTrackerIt = atomMatchingTrackerMap.find(matcherId);
+ if (logTrackerIt == atomMatchingTrackerMap.end()) {
+ ALOGW("cannot find the AtomMatcher \"%lld\" in config", (long long)matcherId);
+ return false;
+ }
+ if (enforceOneAtom && allAtomMatchingTrackers[logTrackerIt->second]->getAtomIds().size() > 1) {
+ ALOGE("AtomMatcher \"%lld\" has more than one tag ids. When a metric has dimension, "
+ "the \"what\" can only be about one atom type. trigger_event matchers can also only "
+ "be about one atom type.",
+ (long long)matcherId);
+ return false;
+ }
+ logTrackerIndex = logTrackerIt->second;
+ auto& metric_list = trackerToMetricMap[logTrackerIndex];
+ metric_list.push_back(metricIndex);
+ return true;
+}
+
+bool handleMetricWithConditions(
+ const int64_t condition, const int metricIndex,
+ const unordered_map<int64_t, int>& conditionTrackerMap,
+ const ::google::protobuf::RepeatedPtrField<::android::os::statsd::MetricConditionLink>&
+ links,
+ const vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex,
+ unordered_map<int, vector<int>>& conditionToMetricMap) {
+ auto condition_it = conditionTrackerMap.find(condition);
+ if (condition_it == conditionTrackerMap.end()) {
+ ALOGW("cannot find Predicate \"%lld\" in the config", (long long)condition);
+ return false;
+ }
+
+ for (const auto& link : links) {
+ auto it = conditionTrackerMap.find(link.condition());
+ if (it == conditionTrackerMap.end()) {
+ ALOGW("cannot find Predicate \"%lld\" in the config", (long long)link.condition());
+ return false;
+ }
+ }
+ conditionIndex = condition_it->second;
+
+ // will create new vector if not exist before.
+ auto& metricList = conditionToMetricMap[condition_it->second];
+ metricList.push_back(metricIndex);
+ return true;
+}
+
+// Initializes state data structures for a metric.
+// input:
+// [config]: the input config
+// [stateIds]: the slice_by_state ids for this metric
+// [stateAtomIdMap]: this map contains the mapping from all state ids to atom ids
+// [allStateGroupMaps]: this map contains the mapping from state ids and state
+// values to state group ids for all states
+// output:
+// [slicedStateAtoms]: a vector of atom ids of all the slice_by_states
+// [stateGroupMap]: this map should contain the mapping from states ids and state
+// values to state group ids for all states that this metric
+// is interested in
+bool handleMetricWithStates(
+ const StatsdConfig& config, const ::google::protobuf::RepeatedField<int64_t>& stateIds,
+ const unordered_map<int64_t, int>& stateAtomIdMap,
+ const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+ vector<int>& slicedStateAtoms,
+ unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) {
+ for (const auto& stateId : stateIds) {
+ auto it = stateAtomIdMap.find(stateId);
+ if (it == stateAtomIdMap.end()) {
+ ALOGW("cannot find State %" PRId64 " in the config", stateId);
+ return false;
+ }
+ int atomId = it->second;
+ slicedStateAtoms.push_back(atomId);
+
+ auto stateIt = allStateGroupMaps.find(stateId);
+ if (stateIt != allStateGroupMaps.end()) {
+ stateGroupMap[atomId] = stateIt->second;
+ }
+ }
+ return true;
+}
+
+bool handleMetricWithStateLink(const FieldMatcher& stateMatcher,
+ const vector<Matcher>& dimensionsInWhat) {
+ vector<Matcher> stateMatchers;
+ translateFieldMatcher(stateMatcher, &stateMatchers);
+
+ return subsetDimensions(stateMatchers, dimensionsInWhat);
+}
+
+// Validates a metricActivation and populates state.
+// EventActivationMap and EventDeactivationMap are supplied to a MetricProducer
+// to provide the producer with state about its activators and deactivators.
+// Returns false if there are errors.
+bool handleMetricActivation(
+ const StatsdConfig& config, const int64_t metricId, const int metricIndex,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation,
+ unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+ unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) {
+ // Check if metric has an associated activation
+ auto itr = metricToActivationMap.find(metricId);
+ if (itr == metricToActivationMap.end()) {
+ return true;
+ }
+
+ int activationIndex = itr->second;
+ const MetricActivation& metricActivation = config.metric_activation(activationIndex);
+
+ for (int i = 0; i < metricActivation.event_activation_size(); i++) {
+ const EventActivation& activation = metricActivation.event_activation(i);
+
+ auto itr = atomMatchingTrackerMap.find(activation.atom_matcher_id());
+ if (itr == atomMatchingTrackerMap.end()) {
+ ALOGE("Atom matcher not found for event activation.");
+ return false;
+ }
+
+ ActivationType activationType = (activation.has_activation_type())
+ ? activation.activation_type()
+ : metricActivation.activation_type();
+ std::shared_ptr<Activation> activationWrapper =
+ std::make_shared<Activation>(activationType, activation.ttl_seconds() * NS_PER_SEC);
+
+ int atomMatcherIndex = itr->second;
+ activationAtomTrackerToMetricMap[atomMatcherIndex].push_back(metricIndex);
+ eventActivationMap.emplace(atomMatcherIndex, activationWrapper);
+
+ if (activation.has_deactivation_atom_matcher_id()) {
+ itr = atomMatchingTrackerMap.find(activation.deactivation_atom_matcher_id());
+ if (itr == atomMatchingTrackerMap.end()) {
+ ALOGE("Atom matcher not found for event deactivation.");
+ return false;
+ }
+ int deactivationAtomMatcherIndex = itr->second;
+ deactivationAtomTrackerToMetricMap[deactivationAtomMatcherIndex].push_back(metricIndex);
+ eventDeactivationMap[deactivationAtomMatcherIndex].push_back(activationWrapper);
+ }
+ }
+
+ metricsWithActivation.push_back(metricIndex);
+ return true;
+}
+
+// Validates a metricActivation and populates state.
+// Fills the new event activation/deactivation maps, preserving the existing activations
+// Returns false if there are errors.
+bool handleMetricActivationOnConfigUpdate(
+ const StatsdConfig& config, const int64_t metricId, const int metricIndex,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const unordered_map<int, shared_ptr<Activation>>& oldEventActivationMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation,
+ unordered_map<int, shared_ptr<Activation>>& newEventActivationMap,
+ unordered_map<int, vector<shared_ptr<Activation>>>& newEventDeactivationMap) {
+ // Check if metric has an associated activation.
+ const auto& itr = metricToActivationMap.find(metricId);
+ if (itr == metricToActivationMap.end()) {
+ return true;
+ }
+
+ int activationIndex = itr->second;
+ const MetricActivation& metricActivation = config.metric_activation(activationIndex);
+
+ for (int i = 0; i < metricActivation.event_activation_size(); i++) {
+ const int64_t activationMatcherId = metricActivation.event_activation(i).atom_matcher_id();
+
+ const auto& newActivationIt = newAtomMatchingTrackerMap.find(activationMatcherId);
+ if (newActivationIt == newAtomMatchingTrackerMap.end()) {
+ ALOGE("Atom matcher not found in new config for event activation.");
+ return false;
+ }
+ int newActivationMatcherIndex = newActivationIt->second;
+
+ // Find the old activation struct and copy it over.
+ const auto& oldActivationIt = oldAtomMatchingTrackerMap.find(activationMatcherId);
+ if (oldActivationIt == oldAtomMatchingTrackerMap.end()) {
+ ALOGE("Atom matcher not found in existing config for event activation.");
+ return false;
+ }
+ int oldActivationMatcherIndex = oldActivationIt->second;
+ const auto& oldEventActivationIt = oldEventActivationMap.find(oldActivationMatcherIndex);
+ if (oldEventActivationIt == oldEventActivationMap.end()) {
+ ALOGE("Could not find existing event activation to update");
+ return false;
+ }
+ newEventActivationMap.emplace(newActivationMatcherIndex, oldEventActivationIt->second);
+ activationAtomTrackerToMetricMap[newActivationMatcherIndex].push_back(metricIndex);
+
+ if (metricActivation.event_activation(i).has_deactivation_atom_matcher_id()) {
+ const int64_t deactivationMatcherId =
+ metricActivation.event_activation(i).deactivation_atom_matcher_id();
+ const auto& newDeactivationIt = newAtomMatchingTrackerMap.find(deactivationMatcherId);
+ if (newDeactivationIt == newAtomMatchingTrackerMap.end()) {
+ ALOGE("Deactivation atom matcher not found in new config for event activation.");
+ return false;
+ }
+ int newDeactivationMatcherIndex = newDeactivationIt->second;
+ newEventDeactivationMap[newDeactivationMatcherIndex].push_back(
+ oldEventActivationIt->second);
+ deactivationAtomTrackerToMetricMap[newDeactivationMatcherIndex].push_back(metricIndex);
+ }
+ }
+
+ metricsWithActivation.push_back(metricIndex);
+ return true;
+}
+
+optional<sp<MetricProducer>> createCountMetricProducerAndUpdateMetadata(
+ const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+ const int64_t currentTimeNs, const CountMetric& metric, const int metricIndex,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<int64_t, int>& conditionTrackerMap,
+ const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+ const unordered_map<int64_t, int>& stateAtomIdMap,
+ const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
+ if (!metric.has_id() || !metric.has_what()) {
+ ALOGE("cannot find metric id or \"what\" in CountMetric \"%lld\"", (long long)metric.id());
+ return nullopt;
+ }
+ int trackerIndex;
+ if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex,
+ metric.has_dimensions_in_what(),
+ allAtomMatchingTrackers, atomMatchingTrackerMap,
+ trackerToMetricMap, trackerIndex)) {
+ return nullopt;
+ }
+
+ int conditionIndex = -1;
+ if (metric.has_condition()) {
+ if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+ metric.links(), allConditionTrackers, conditionIndex,
+ conditionToMetricMap)) {
+ return nullopt;
+ }
+ } else {
+ if (metric.links_size() > 0) {
+ ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
+ return nullopt;
+ }
+ }
+
+ std::vector<int> slicedStateAtoms;
+ unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
+ if (metric.slice_by_state_size() > 0) {
+ if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
+ allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
+ return nullopt;
+ }
+ } else {
+ if (metric.state_link_size() > 0) {
+ ALOGW("CountMetric has a MetricStateLink but doesn't have a slice_by_state");
+ return nullopt;
+ }
+ }
+
+ unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+ if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap,
+ atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation,
+ eventActivationMap, eventDeactivationMap)) {
+ return nullopt;
+ }
+
+ uint64_t metricHash;
+ if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+ return nullopt;
+ }
+
+ return {new CountMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
+ metricHash, timeBaseNs, currentTimeNs, eventActivationMap,
+ eventDeactivationMap, slicedStateAtoms, stateGroupMap)};
+}
+
+optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata(
+ const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+ const int64_t currentTimeNs, const DurationMetric& metric, const int metricIndex,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<int64_t, int>& conditionTrackerMap,
+ const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+ const unordered_map<int64_t, int>& stateAtomIdMap,
+ const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
+ if (!metric.has_id() || !metric.has_what()) {
+ ALOGE("cannot find metric id or \"what\" in DurationMetric \"%lld\"",
+ (long long)metric.id());
+ return nullopt;
+ }
+ const auto& what_it = conditionTrackerMap.find(metric.what());
+ if (what_it == conditionTrackerMap.end()) {
+ ALOGE("DurationMetric's \"what\" is not present in the condition trackers");
+ return nullopt;
+ }
+
+ const int whatIndex = what_it->second;
+ const Predicate& durationWhat = config.predicate(whatIndex);
+ if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) {
+ ALOGE("DurationMetric's \"what\" must be a simple condition");
+ return nullopt;
+ }
+
+ const SimplePredicate& simplePredicate = durationWhat.simple_predicate();
+ bool nesting = simplePredicate.count_nesting();
+
+ int startIndex = -1, stopIndex = -1, stopAllIndex = -1;
+ if (!simplePredicate.has_start() ||
+ !handleMetricWithAtomMatchingTrackers(
+ simplePredicate.start(), metricIndex, metric.has_dimensions_in_what(),
+ allAtomMatchingTrackers, atomMatchingTrackerMap, trackerToMetricMap, startIndex)) {
+ ALOGE("Duration metrics must specify a valid start event matcher");
+ return nullopt;
+ }
+
+ if (simplePredicate.has_stop() &&
+ !handleMetricWithAtomMatchingTrackers(
+ simplePredicate.stop(), metricIndex, metric.has_dimensions_in_what(),
+ allAtomMatchingTrackers, atomMatchingTrackerMap, trackerToMetricMap, stopIndex)) {
+ return nullopt;
+ }
+
+ if (simplePredicate.has_stop_all() &&
+ !handleMetricWithAtomMatchingTrackers(simplePredicate.stop_all(), metricIndex,
+ metric.has_dimensions_in_what(),
+ allAtomMatchingTrackers, atomMatchingTrackerMap,
+ trackerToMetricMap, stopAllIndex)) {
+ return nullopt;
+ }
+
+ FieldMatcher internalDimensions = simplePredicate.dimensions();
+
+ int conditionIndex = -1;
+ if (metric.has_condition()) {
+ if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+ metric.links(), allConditionTrackers, conditionIndex,
+ conditionToMetricMap)) {
+ return nullopt;
+ }
+ } else if (metric.links_size() > 0) {
+ ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
+ return nullopt;
+ }
+
+ std::vector<int> slicedStateAtoms;
+ unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
+ if (metric.slice_by_state_size() > 0) {
+ if (metric.aggregation_type() == DurationMetric::MAX_SPARSE) {
+ ALOGE("DurationMetric with aggregation type MAX_SPARSE cannot be sliced by state");
+ return nullopt;
+ }
+ if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
+ allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
+ return nullopt;
+ }
+ } else if (metric.state_link_size() > 0) {
+ ALOGW("DurationMetric has a MetricStateLink but doesn't have a sliced state");
+ return nullopt;
+ }
+
+ // Check that all metric state links are a subset of dimensions_in_what fields.
+ std::vector<Matcher> dimensionsInWhat;
+ translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
+ for (const auto& stateLink : metric.state_link()) {
+ if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) {
+ ALOGW("DurationMetric's MetricStateLinks must be a subset of dimensions in what");
+ return nullopt;
+ }
+ }
+
+ unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+ if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap,
+ atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation,
+ eventActivationMap, eventDeactivationMap)) {
+ return nullopt;
+ }
+
+ uint64_t metricHash;
+ if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+ return nullopt;
+ }
+
+ sp<MetricProducer> producer = new DurationMetricProducer(
+ key, metric, conditionIndex, initialConditionCache, whatIndex, startIndex, stopIndex,
+ stopAllIndex, nesting, wizard, metricHash, internalDimensions, timeBaseNs,
+ currentTimeNs, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
+ stateGroupMap);
+ if (!producer->isValid()) {
+ return nullopt;
+ }
+ return {producer};
+}
+
+optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata(
+ const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+ const EventMetric& metric, const int metricIndex,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<int64_t, int>& conditionTrackerMap,
+ const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
+ if (!metric.has_id() || !metric.has_what()) {
+ ALOGE("cannot find the metric name or what in config");
+ return nullopt;
+ }
+ int trackerIndex;
+ if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false,
+ allAtomMatchingTrackers, atomMatchingTrackerMap,
+ trackerToMetricMap, trackerIndex)) {
+ return nullopt;
+ }
+
+ int conditionIndex = -1;
+ if (metric.has_condition()) {
+ if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+ metric.links(), allConditionTrackers, conditionIndex,
+ conditionToMetricMap)) {
+ return nullopt;
+ }
+ } else {
+ if (metric.links_size() > 0) {
+ ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
+ return nullopt;
+ }
+ }
+
+ unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+ bool success = handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap,
+ atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation,
+ eventActivationMap, eventDeactivationMap);
+ if (!success) return nullptr;
+
+ uint64_t metricHash;
+ if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+ return nullopt;
+ }
+
+ return {new EventMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
+ metricHash, timeBaseNs, eventActivationMap,
+ eventDeactivationMap)};
+}
+
+optional<sp<MetricProducer>> createValueMetricProducerAndUpdateMetadata(
+ const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+ const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const ValueMetric& metric, const int metricIndex,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<int64_t, int>& conditionTrackerMap,
+ const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const unordered_map<int64_t, int>& stateAtomIdMap,
+ const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
+ if (!metric.has_id() || !metric.has_what()) {
+ ALOGE("cannot find metric id or \"what\" in ValueMetric \"%lld\"", (long long)metric.id());
+ return nullopt;
+ }
+ if (!metric.has_value_field()) {
+ ALOGE("cannot find \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id());
+ return nullopt;
+ }
+ std::vector<Matcher> fieldMatchers;
+ translateFieldMatcher(metric.value_field(), &fieldMatchers);
+ if (fieldMatchers.size() < 1) {
+ ALOGE("incorrect \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id());
+ return nullopt;
+ }
+
+ int trackerIndex;
+ if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex,
+ metric.has_dimensions_in_what(),
+ allAtomMatchingTrackers, atomMatchingTrackerMap,
+ trackerToMetricMap, trackerIndex)) {
+ return nullopt;
+ }
+
+ sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
+ // If it is pulled atom, it should be simple matcher with one tagId.
+ if (atomMatcher->getAtomIds().size() != 1) {
+ return nullopt;
+ }
+ int atomTagId = *(atomMatcher->getAtomIds().begin());
+ int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
+
+ int conditionIndex = -1;
+ if (metric.has_condition()) {
+ if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+ metric.links(), allConditionTrackers, conditionIndex,
+ conditionToMetricMap)) {
+ return nullopt;
+ }
+ } else if (metric.links_size() > 0) {
+ ALOGE("metrics has a MetricConditionLink but doesn't have a condition");
+ return nullopt;
+ }
+
+ std::vector<int> slicedStateAtoms;
+ unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
+ if (metric.slice_by_state_size() > 0) {
+ if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
+ allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
+ return nullopt;
+ }
+ } else if (metric.state_link_size() > 0) {
+ ALOGE("ValueMetric has a MetricStateLink but doesn't have a sliced state");
+ return nullopt;
+ }
+
+ // Check that all metric state links are a subset of dimensions_in_what fields.
+ std::vector<Matcher> dimensionsInWhat;
+ translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
+ for (const auto& stateLink : metric.state_link()) {
+ if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) {
+ ALOGW("ValueMetric's MetricStateLinks must be a subset of the dimensions in what");
+ return nullopt;
+ }
+ }
+
+ unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+ if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap,
+ atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation,
+ eventActivationMap, eventDeactivationMap)) {
+ return nullopt;
+ }
+
+ uint64_t metricHash;
+ if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+ return nullopt;
+ }
+
+ return {new ValueMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
+ metricHash, trackerIndex, matcherWizard, pullTagId, timeBaseNs,
+ currentTimeNs, pullerManager, eventActivationMap,
+ eventDeactivationMap, slicedStateAtoms, stateGroupMap)};
+}
+
+optional<sp<MetricProducer>> createGaugeMetricProducerAndUpdateMetadata(
+ const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+ const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const GaugeMetric& metric, const int metricIndex,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<int64_t, int>& conditionTrackerMap,
+ const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
+ if (!metric.has_id() || !metric.has_what()) {
+ ALOGE("cannot find metric id or \"what\" in GaugeMetric \"%lld\"", (long long)metric.id());
+ return nullopt;
+ }
+
+ if ((!metric.gauge_fields_filter().has_include_all() ||
+ (metric.gauge_fields_filter().include_all() == false)) &&
+ !hasLeafNode(metric.gauge_fields_filter().fields())) {
+ ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
+ return nullopt;
+ }
+ if ((metric.gauge_fields_filter().has_include_all() &&
+ metric.gauge_fields_filter().include_all() == true) &&
+ hasLeafNode(metric.gauge_fields_filter().fields())) {
+ ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
+ return nullopt;
+ }
+
+ int trackerIndex;
+ if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex,
+ metric.has_dimensions_in_what(),
+ allAtomMatchingTrackers, atomMatchingTrackerMap,
+ trackerToMetricMap, trackerIndex)) {
+ return nullopt;
+ }
+
+ sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
+ // For GaugeMetric atom, it should be simple matcher with one tagId.
+ if (atomMatcher->getAtomIds().size() != 1) {
+ return nullopt;
+ }
+ int atomTagId = *(atomMatcher->getAtomIds().begin());
+ int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
+
+ int triggerTrackerIndex;
+ int triggerAtomId = -1;
+ if (metric.has_trigger_event()) {
+ if (pullTagId == -1) {
+ ALOGW("Pull atom not specified for trigger");
+ return nullopt;
+ }
+ // trigger_event should be used with FIRST_N_SAMPLES
+ if (metric.sampling_type() != GaugeMetric::FIRST_N_SAMPLES) {
+ ALOGW("Gauge Metric with trigger event must have sampling type FIRST_N_SAMPLES");
+ return nullopt;
+ }
+ if (!handleMetricWithAtomMatchingTrackers(metric.trigger_event(), metricIndex,
+ /*enforceOneAtom=*/true, allAtomMatchingTrackers,
+ atomMatchingTrackerMap, trackerToMetricMap,
+ triggerTrackerIndex)) {
+ return nullopt;
+ }
+ sp<AtomMatchingTracker> triggerAtomMatcher =
+ allAtomMatchingTrackers.at(triggerTrackerIndex);
+ triggerAtomId = *(triggerAtomMatcher->getAtomIds().begin());
+ }
+
+ if (!metric.has_trigger_event() && pullTagId != -1 &&
+ metric.sampling_type() == GaugeMetric::FIRST_N_SAMPLES) {
+ ALOGW("FIRST_N_SAMPLES is only for pushed event or pull_on_trigger");
+ return nullopt;
+ }
+
+ int conditionIndex = -1;
+ if (metric.has_condition()) {
+ if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+ metric.links(), allConditionTrackers, conditionIndex,
+ conditionToMetricMap)) {
+ return nullopt;
+ }
+ } else {
+ if (metric.links_size() > 0) {
+ ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
+ return nullopt;
+ }
+ }
+
+ unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+ if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap,
+ atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation,
+ eventActivationMap, eventDeactivationMap)) {
+ return nullopt;
+ }
+
+ uint64_t metricHash;
+ if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+ return nullopt;
+ }
+
+ return {new GaugeMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
+ metricHash, trackerIndex, matcherWizard, pullTagId,
+ triggerAtomId, atomTagId, timeBaseNs, currentTimeNs,
+ pullerManager, eventActivationMap, eventDeactivationMap)};
+}
+
+optional<sp<AnomalyTracker>> createAnomalyTracker(
+ const Alert& alert, const sp<AlarmMonitor>& anomalyAlarmMonitor,
+ const unordered_map<int64_t, int>& metricProducerMap,
+ vector<sp<MetricProducer>>& allMetricProducers) {
+ const auto& itr = metricProducerMap.find(alert.metric_id());
+ if (itr == metricProducerMap.end()) {
+ ALOGW("alert \"%lld\" has unknown metric id: \"%lld\"", (long long)alert.id(),
+ (long long)alert.metric_id());
+ return nullopt;
+ }
+ if (!alert.has_trigger_if_sum_gt()) {
+ ALOGW("invalid alert: missing threshold");
+ return nullopt;
+ }
+ if (alert.trigger_if_sum_gt() < 0 || alert.num_buckets() <= 0) {
+ ALOGW("invalid alert: threshold=%f num_buckets= %d", alert.trigger_if_sum_gt(),
+ alert.num_buckets());
+ return nullopt;
+ }
+ const int metricIndex = itr->second;
+ sp<MetricProducer> metric = allMetricProducers[metricIndex];
+ sp<AnomalyTracker> anomalyTracker = metric->addAnomalyTracker(alert, anomalyAlarmMonitor);
+ if (anomalyTracker == nullptr) {
+ // The ALOGW for this invalid alert was already displayed in addAnomalyTracker().
+ return nullopt;
+ }
+ return {anomalyTracker};
+}
+
+bool initAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
+ unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ set<int>& allTagIds) {
+ vector<AtomMatcher> matcherConfigs;
+ const int atomMatcherCount = config.atom_matcher_size();
+ matcherConfigs.reserve(atomMatcherCount);
+ allAtomMatchingTrackers.reserve(atomMatcherCount);
+
+ for (int i = 0; i < atomMatcherCount; i++) {
+ const AtomMatcher& logMatcher = config.atom_matcher(i);
+ sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(logMatcher, i, uidMap);
+ if (tracker == nullptr) {
+ return false;
+ }
+ allAtomMatchingTrackers.push_back(tracker);
+ if (atomMatchingTrackerMap.find(logMatcher.id()) != atomMatchingTrackerMap.end()) {
+ ALOGE("Duplicate AtomMatcher found!");
+ return false;
+ }
+ atomMatchingTrackerMap[logMatcher.id()] = i;
+ matcherConfigs.push_back(logMatcher);
+ }
+
+ vector<bool> stackTracker2(allAtomMatchingTrackers.size(), false);
+ for (auto& matcher : allAtomMatchingTrackers) {
+ if (!matcher->init(matcherConfigs, allAtomMatchingTrackers, atomMatchingTrackerMap,
+ stackTracker2)) {
+ return false;
+ }
+ // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only.
+ const set<int>& tagIds = matcher->getAtomIds();
+ allTagIds.insert(tagIds.begin(), tagIds.end());
+ }
+ return true;
+}
+
+bool initConditions(const ConfigKey& key, const StatsdConfig& config,
+ const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ unordered_map<int64_t, int>& conditionTrackerMap,
+ vector<sp<ConditionTracker>>& allConditionTrackers,
+ unordered_map<int, std::vector<int>>& trackerToConditionMap,
+ vector<ConditionState>& initialConditionCache) {
+ vector<Predicate> conditionConfigs;
+ const int conditionTrackerCount = config.predicate_size();
+ conditionConfigs.reserve(conditionTrackerCount);
+ allConditionTrackers.reserve(conditionTrackerCount);
+ initialConditionCache.assign(conditionTrackerCount, ConditionState::kNotEvaluated);
+
+ for (int i = 0; i < conditionTrackerCount; i++) {
+ const Predicate& condition = config.predicate(i);
+ sp<ConditionTracker> tracker =
+ createConditionTracker(key, condition, i, atomMatchingTrackerMap);
+ if (tracker == nullptr) {
+ return false;
+ }
+ allConditionTrackers.push_back(tracker);
+ if (conditionTrackerMap.find(condition.id()) != conditionTrackerMap.end()) {
+ ALOGE("Duplicate Predicate found!");
+ return false;
+ }
+ conditionTrackerMap[condition.id()] = i;
+ conditionConfigs.push_back(condition);
+ }
+
+ vector<bool> stackTracker(allConditionTrackers.size(), false);
+ for (size_t i = 0; i < allConditionTrackers.size(); i++) {
+ auto& conditionTracker = allConditionTrackers[i];
+ if (!conditionTracker->init(conditionConfigs, allConditionTrackers, conditionTrackerMap,
+ stackTracker, initialConditionCache)) {
+ return false;
+ }
+ for (const int trackerIndex : conditionTracker->getAtomMatchingTrackerIndex()) {
+ auto& conditionList = trackerToConditionMap[trackerIndex];
+ conditionList.push_back(i);
+ }
+ }
+ return true;
+}
+
+bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
+ unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+ map<int64_t, uint64_t>& stateProtoHashes) {
+ for (int i = 0; i < config.state_size(); i++) {
+ const State& state = config.state(i);
+ const int64_t stateId = state.id();
+ stateAtomIdMap[stateId] = state.atom_id();
+
+ string serializedState;
+ if (!state.SerializeToString(&serializedState)) {
+ ALOGE("Unable to serialize state %lld", (long long)stateId);
+ return false;
+ }
+ stateProtoHashes[stateId] = Hash64(serializedState);
+
+ const StateMap& stateMap = state.map();
+ for (auto group : stateMap.group()) {
+ for (auto value : group.value()) {
+ allStateGroupMaps[stateId][value] = group.group_id();
+ }
+ }
+ }
+
+ return true;
+}
+
+bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs,
+ const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ const unordered_map<int64_t, int>& conditionTrackerMap,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& stateAtomIdMap,
+ const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+ vector<sp<ConditionTracker>>& allConditionTrackers,
+ const vector<ConditionState>& initialConditionCache,
+ vector<sp<MetricProducer>>& allMetricProducers,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int64_t, int>& metricMap, std::set<int64_t>& noReportMetricIds,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
+ sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
+ sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchingTrackers);
+ const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
+ config.event_metric_size() + config.gauge_metric_size() +
+ config.value_metric_size();
+ allMetricProducers.reserve(allMetricsCount);
+
+ // Construct map from metric id to metric activation index. The map will be used to determine
+ // the metric activation corresponding to a metric.
+ unordered_map<int64_t, int> metricToActivationMap;
+ for (int i = 0; i < config.metric_activation_size(); i++) {
+ const MetricActivation& metricActivation = config.metric_activation(i);
+ int64_t metricId = metricActivation.metric_id();
+ if (metricToActivationMap.find(metricId) != metricToActivationMap.end()) {
+ ALOGE("Metric %lld has multiple MetricActivations", (long long)metricId);
+ return false;
+ }
+ metricToActivationMap.insert({metricId, i});
+ }
+
+ // Build MetricProducers for each metric defined in config.
+ // build CountMetricProducer
+ for (int i = 0; i < config.count_metric_size(); i++) {
+ int metricIndex = allMetricProducers.size();
+ const CountMetric& metric = config.count_metric(i);
+ metricMap.insert({metric.id(), metricIndex});
+ optional<sp<MetricProducer>> producer = createCountMetricProducerAndUpdateMetadata(
+ key, config, timeBaseTimeNs, currentTimeNs, metric, metricIndex,
+ allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers,
+ conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
+ allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation);
+ if (!producer) {
+ return false;
+ }
+ allMetricProducers.push_back(producer.value());
+ }
+
+ // build DurationMetricProducer
+ for (int i = 0; i < config.duration_metric_size(); i++) {
+ int metricIndex = allMetricProducers.size();
+ const DurationMetric& metric = config.duration_metric(i);
+ metricMap.insert({metric.id(), metricIndex});
+
+ optional<sp<MetricProducer>> producer = createDurationMetricProducerAndUpdateMetadata(
+ key, config, timeBaseTimeNs, currentTimeNs, metric, metricIndex,
+ allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers,
+ conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
+ allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation);
+ if (!producer) {
+ return false;
+ }
+ allMetricProducers.push_back(producer.value());
+ }
+
+ // build EventMetricProducer
+ for (int i = 0; i < config.event_metric_size(); i++) {
+ int metricIndex = allMetricProducers.size();
+ const EventMetric& metric = config.event_metric(i);
+ metricMap.insert({metric.id(), metricIndex});
+ optional<sp<MetricProducer>> producer = createEventMetricProducerAndUpdateMetadata(
+ key, config, timeBaseTimeNs, metric, metricIndex, allAtomMatchingTrackers,
+ atomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap,
+ initialConditionCache, wizard, metricToActivationMap, trackerToMetricMap,
+ conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation);
+ if (!producer) {
+ return false;
+ }
+ allMetricProducers.push_back(producer.value());
+ }
+
+ // build ValueMetricProducer
+ for (int i = 0; i < config.value_metric_size(); i++) {
+ int metricIndex = allMetricProducers.size();
+ const ValueMetric& metric = config.value_metric(i);
+ metricMap.insert({metric.id(), metricIndex});
+ optional<sp<MetricProducer>> producer = createValueMetricProducerAndUpdateMetadata(
+ key, config, timeBaseTimeNs, currentTimeNs, pullerManager, metric, metricIndex,
+ allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers,
+ conditionTrackerMap, initialConditionCache, wizard, matcherWizard, stateAtomIdMap,
+ allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation);
+ if (!producer) {
+ return false;
+ }
+ allMetricProducers.push_back(producer.value());
+ }
+
+ // Gauge metrics.
+ for (int i = 0; i < config.gauge_metric_size(); i++) {
+ int metricIndex = allMetricProducers.size();
+ const GaugeMetric& metric = config.gauge_metric(i);
+ metricMap.insert({metric.id(), metricIndex});
+ optional<sp<MetricProducer>> producer = createGaugeMetricProducerAndUpdateMetadata(
+ key, config, timeBaseTimeNs, currentTimeNs, pullerManager, metric, metricIndex,
+ allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers,
+ conditionTrackerMap, initialConditionCache, wizard, matcherWizard,
+ metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation);
+ if (!producer) {
+ return false;
+ }
+ allMetricProducers.push_back(producer.value());
+ }
+ for (int i = 0; i < config.no_report_metric_size(); ++i) {
+ const auto no_report_metric = config.no_report_metric(i);
+ if (metricMap.find(no_report_metric) == metricMap.end()) {
+ ALOGW("no_report_metric %" PRId64 " not exist", no_report_metric);
+ return false;
+ }
+ noReportMetricIds.insert(no_report_metric);
+ }
+
+ const set<int> whitelistedAtomIds(config.whitelisted_atom_ids().begin(),
+ config.whitelisted_atom_ids().end());
+ for (const auto& it : allMetricProducers) {
+ // Register metrics to StateTrackers
+ for (int atomId : it->getSlicedStateAtoms()) {
+ // Register listener for non-whitelisted atoms only. Using whitelisted atom as a sliced
+ // state atom is not allowed.
+ if (whitelistedAtomIds.find(atomId) == whitelistedAtomIds.end()) {
+ StateManager::getInstance().registerListener(atomId, it);
+ } else {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool initAlerts(const StatsdConfig& config, const unordered_map<int64_t, int>& metricProducerMap,
+ unordered_map<int64_t, int>& alertTrackerMap,
+ const sp<AlarmMonitor>& anomalyAlarmMonitor,
+ vector<sp<MetricProducer>>& allMetricProducers,
+ vector<sp<AnomalyTracker>>& allAnomalyTrackers) {
+ for (int i = 0; i < config.alert_size(); i++) {
+ const Alert& alert = config.alert(i);
+ alertTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size()));
+ optional<sp<AnomalyTracker>> anomalyTracker = createAnomalyTracker(
+ alert, anomalyAlarmMonitor, metricProducerMap, allMetricProducers);
+ if (!anomalyTracker) {
+ return false;
+ }
+ allAnomalyTrackers.push_back(anomalyTracker.value());
+ }
+ if (!initSubscribersForSubscriptionType(config, Subscription::ALERT, alertTrackerMap,
+ allAnomalyTrackers)) {
+ return false;
+ }
+ return true;
+}
+
+bool initAlarms(const StatsdConfig& config, const ConfigKey& key,
+ const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
+ const int64_t currentTimeNs, vector<sp<AlarmTracker>>& allAlarmTrackers) {
+ unordered_map<int64_t, int> alarmTrackerMap;
+ int64_t startMillis = timeBaseNs / 1000 / 1000;
+ int64_t currentTimeMillis = currentTimeNs / 1000 / 1000;
+ for (int i = 0; i < config.alarm_size(); i++) {
+ const Alarm& alarm = config.alarm(i);
+ if (alarm.offset_millis() <= 0) {
+ ALOGW("Alarm offset_millis should be larger than 0.");
+ return false;
+ }
+ if (alarm.period_millis() <= 0) {
+ ALOGW("Alarm period_millis should be larger than 0.");
+ return false;
+ }
+ alarmTrackerMap.insert(std::make_pair(alarm.id(), allAlarmTrackers.size()));
+ allAlarmTrackers.push_back(
+ new AlarmTracker(startMillis, currentTimeMillis, alarm, key, periodicAlarmMonitor));
+ }
+ if (!initSubscribersForSubscriptionType(config, Subscription::ALARM, alarmTrackerMap,
+ allAlarmTrackers)) {
+ return false;
+ }
+ return true;
+}
+
+bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,
+ const sp<StatsPullerManager>& pullerManager,
+ const sp<AlarmMonitor>& anomalyAlarmMonitor,
+ const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
+ const int64_t currentTimeNs, set<int>& allTagIds,
+ vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ vector<sp<ConditionTracker>>& allConditionTrackers,
+ unordered_map<int64_t, int>& conditionTrackerMap,
+ vector<sp<MetricProducer>>& allMetricProducers,
+ unordered_map<int64_t, int>& metricProducerMap,
+ vector<sp<AnomalyTracker>>& allAnomalyTrackers,
+ vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers,
+ unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ unordered_map<int, std::vector<int>>& trackerToConditionMap,
+ unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ unordered_map<int64_t, int>& alertTrackerMap,
+ vector<int>& metricsWithActivation, map<int64_t, uint64_t>& stateProtoHashes,
+ set<int64_t>& noReportMetricIds) {
+ vector<ConditionState> initialConditionCache;
+ unordered_map<int64_t, int> stateAtomIdMap;
+ unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
+
+ if (!initAtomMatchingTrackers(config, uidMap, atomMatchingTrackerMap, allAtomMatchingTrackers,
+ allTagIds)) {
+ ALOGE("initAtomMatchingTrackers failed");
+ return false;
+ }
+ VLOG("initAtomMatchingTrackers succeed...");
+
+ if (!initConditions(key, config, atomMatchingTrackerMap, conditionTrackerMap,
+ allConditionTrackers, trackerToConditionMap, initialConditionCache)) {
+ ALOGE("initConditionTrackers failed");
+ return false;
+ }
+
+ if (!initStates(config, stateAtomIdMap, allStateGroupMaps, stateProtoHashes)) {
+ ALOGE("initStates failed");
+ return false;
+ }
+ if (!initMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager, atomMatchingTrackerMap,
+ conditionTrackerMap, allAtomMatchingTrackers, stateAtomIdMap,
+ allStateGroupMaps, allConditionTrackers, initialConditionCache,
+ allMetricProducers, conditionToMetricMap, trackerToMetricMap,
+ metricProducerMap, noReportMetricIds, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+ ALOGE("initMetricProducers failed");
+ return false;
+ }
+ if (!initAlerts(config, metricProducerMap, alertTrackerMap, anomalyAlarmMonitor,
+ allMetricProducers, allAnomalyTrackers)) {
+ ALOGE("initAlerts failed");
+ return false;
+ }
+ if (!initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs,
+ allPeriodicAlarmTrackers)) {
+ ALOGE("initAlarms failed");
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/bin/src/metrics/parsing_utils/metrics_manager_util.h b/bin/src/metrics/parsing_utils/metrics_manager_util.h
new file mode 100644
index 0000000..84e1e4e
--- /dev/null
+++ b/bin/src/metrics/parsing_utils/metrics_manager_util.h
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#pragma once
+
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+#include "anomaly/AlarmTracker.h"
+#include "condition/ConditionTracker.h"
+#include "external/StatsPullerManager.h"
+#include "matchers/AtomMatchingTracker.h"
+#include "metrics/MetricProducer.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Helper functions for creating, validating, and updating config components from StatsdConfig.
+// Should only be called from metrics_manager_util and config_update_utils.
+
+// Create a AtomMatchingTracker.
+// input:
+// [logMatcher]: the input AtomMatcher from the StatsdConfig
+// [index]: the index of the matcher
+// output:
+// new AtomMatchingTracker, or null if the tracker is unable to be created
+sp<AtomMatchingTracker> createAtomMatchingTracker(const AtomMatcher& logMatcher, const int index,
+ const sp<UidMap>& uidMap);
+
+// Create a ConditionTracker.
+// input:
+// [predicate]: the input Predicate from the StatsdConfig
+// [index]: the index of the condition tracker
+// [atomMatchingTrackerMap]: map of atom matcher id to its index in allAtomMatchingTrackers
+// output:
+// new ConditionTracker, or null if the tracker is unable to be created
+sp<ConditionTracker> createConditionTracker(
+ const ConfigKey& key, const Predicate& predicate, const int index,
+ const unordered_map<int64_t, int>& atomMatchingTrackerMap);
+
+// Get the hash of a metric, combining the activation if the metric has one.
+bool getMetricProtoHash(const StatsdConfig& config, const google::protobuf::MessageLite& metric,
+ const int64_t id,
+ const std::unordered_map<int64_t, int>& metricToActivationMap,
+ uint64_t& metricHash);
+
+// 1. Validates matcher existence
+// 2. Enforces matchers with dimensions and those used for trigger_event are about one atom
+// 3. Gets matcher index and updates tracker to metric map
+bool handleMetricWithAtomMatchingTrackers(
+ const int64_t matcherId, const int metricIndex, const bool enforceOneAtom,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap, int& logTrackerIndex);
+
+// 1. Validates condition existence, including those in links
+// 2. Gets condition index and updates condition to metric map
+bool handleMetricWithConditions(
+ const int64_t condition, const int metricIndex,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const ::google::protobuf::RepeatedPtrField<::android::os::statsd::MetricConditionLink>&
+ links,
+ const std::vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap);
+
+// Validates a metricActivation and populates state.
+// Fills the new event activation/deactivation maps, preserving the existing activations.
+// Returns false if there are errors.
+bool handleMetricActivationOnConfigUpdate(
+ const StatsdConfig& config, const int64_t metricId, const int metricIndex,
+ const std::unordered_map<int64_t, int>& metricToActivationMap,
+ const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const std::unordered_map<int, shared_ptr<Activation>>& oldEventActivationMap,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation,
+ std::unordered_map<int, shared_ptr<Activation>>& newEventActivationMap,
+ std::unordered_map<int, std::vector<shared_ptr<Activation>>>& newEventDeactivationMap);
+
+// Creates a CountMetricProducer and updates the vectors/maps used by MetricsManager with
+// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
+optional<sp<MetricProducer>> createCountMetricProducerAndUpdateMetadata(
+ const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+ const int64_t currentTimeNs, const CountMetric& metric, const int metricIndex,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+ const std::unordered_map<int64_t, int>& stateAtomIdMap,
+ const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps,
+ const std::unordered_map<int64_t, int>& metricToActivationMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation);
+
+// Creates a DurationMetricProducer and updates the vectors/maps used by MetricsManager with
+// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
+optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata(
+ const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+ const int64_t currentTimeNs, const DurationMetric& metric, const int metricIndex,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+ const std::unordered_map<int64_t, int>& stateAtomIdMap,
+ const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps,
+ const std::unordered_map<int64_t, int>& metricToActivationMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation);
+
+// Creates an EventMetricProducer and updates the vectors/maps used by MetricsManager with
+// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
+optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata(
+ const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+ const EventMetric& metric, const int metricIndex,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+ const std::unordered_map<int64_t, int>& metricToActivationMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation);
+
+// Creates a CountMetricProducer and updates the vectors/maps used by MetricsManager with
+// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
+optional<sp<MetricProducer>> createValueMetricProducerAndUpdateMetadata(
+ const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+ const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const ValueMetric& metric, const int metricIndex,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const std::unordered_map<int64_t, int>& stateAtomIdMap,
+ const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps,
+ const std::unordered_map<int64_t, int>& metricToActivationMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation);
+
+// Creates a GaugeMetricProducer and updates the vectors/maps used by MetricsManager with
+// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
+optional<sp<MetricProducer>> createGaugeMetricProducerAndUpdateMetadata(
+ const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+ const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const GaugeMetric& metric, const int metricIndex,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const std::unordered_map<int64_t, int>& metricToActivationMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation);
+
+// Creates an AnomalyTracker and adds it to the appropriate metric.
+// Returns an sp to the AnomalyTracker, or nullopt if there was an error.
+optional<sp<AnomalyTracker>> createAnomalyTracker(
+ const Alert& alert, const sp<AlarmMonitor>& anomalyAlarmMonitor,
+ const std::unordered_map<int64_t, int>& metricProducerMap,
+ std::vector<sp<MetricProducer>>& allMetricProducers);
+
+// Templated function for adding subscriptions to alarms or alerts. Returns true if successful.
+template <typename T>
+bool initSubscribersForSubscriptionType(const StatsdConfig& config,
+ const Subscription_RuleType ruleType,
+ const std::unordered_map<int64_t, int>& ruleMap,
+ std::vector<T>& allRules) {
+ for (int i = 0; i < config.subscription_size(); ++i) {
+ const Subscription& subscription = config.subscription(i);
+ if (subscription.rule_type() != ruleType) {
+ continue;
+ }
+ if (subscription.subscriber_information_case() ==
+ Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) {
+ ALOGW("subscription \"%lld\" has no subscriber info.\"", (long long)subscription.id());
+ return false;
+ }
+ const auto& itr = ruleMap.find(subscription.rule_id());
+ if (itr == ruleMap.end()) {
+ ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"",
+ (long long)subscription.id(), (long long)subscription.rule_id());
+ return false;
+ }
+ const int ruleIndex = itr->second;
+ allRules[ruleIndex]->addSubscription(subscription);
+ }
+ return true;
+}
+
+// Helper functions for MetricsManager to initialize from StatsdConfig.
+// *Note*: only initStatsdConfig() should be called from outside.
+// All other functions are intermediate
+// steps, created to make unit tests easier. And most of the parameters in these
+// functions are temporary objects in the initialization phase.
+
+// Initialize the AtomMatchingTrackers.
+// input:
+// [key]: the config key that this config belongs to
+// [config]: the input StatsdConfig
+// output:
+// [atomMatchingTrackerMap]: this map should contain matcher name to index mapping
+// [allAtomMatchingTrackers]: should store the sp to all the AtomMatchingTracker
+// [allTagIds]: contains the set of all interesting tag ids to this config.
+bool initAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
+ std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ std::set<int>& allTagIds);
+
+// Initialize ConditionTrackers
+// input:
+// [key]: the config key that this config belongs to
+// [config]: the input config
+// [atomMatchingTrackerMap]: AtomMatchingTracker name to index mapping from previous step.
+// output:
+// [conditionTrackerMap]: this map should contain condition name to index mapping
+// [allConditionTrackers]: stores the sp to all the ConditionTrackers
+// [trackerToConditionMap]: contain the mapping from index of
+// log tracker to condition trackers that use the log tracker
+// [initialConditionCache]: stores the initial conditions for each ConditionTracker
+bool initConditions(const ConfigKey& key, const StatsdConfig& config,
+ const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ std::unordered_map<int64_t, int>& conditionTrackerMap,
+ std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
+ std::vector<ConditionState>& initialConditionCache);
+
+// Initialize State maps using State protos in the config. These maps will
+// eventually be passed to MetricProducers to initialize their state info.
+// input:
+// [config]: the input config
+// output:
+// [stateAtomIdMap]: this map should contain the mapping from state ids to atom ids
+// [allStateGroupMaps]: this map should contain the mapping from states ids and state
+// values to state group ids for all states
+// [stateProtoHashes]: contains a map of state id to the hash of the State proto from the config
+bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
+ unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+ std::map<int64_t, uint64_t>& stateProtoHashes);
+
+// Initialize MetricProducers.
+// input:
+// [key]: the config key that this config belongs to
+// [config]: the input config
+// [timeBaseSec]: start time base for all metrics
+// [atomMatchingTrackerMap]: AtomMatchingTracker name to index mapping from previous step.
+// [conditionTrackerMap]: condition name to index mapping
+// [stateAtomIdMap]: contains the mapping from state ids to atom ids
+// [allStateGroupMaps]: contains the mapping from atom ids and state values to
+// state group ids for all states
+// output:
+// [allMetricProducers]: contains the list of sp to the MetricProducers created.
+// [conditionToMetricMap]: contains the mapping from condition tracker index to
+// the list of MetricProducer index
+// [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
+bool initMetrics(
+ const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs,
+ const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& stateAtomIdMap,
+ const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+ vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::vector<ConditionState>& initialConditionCache,
+ std::vector<sp<MetricProducer>>& allMetricProducers,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::set<int64_t>& noReportMetricIds,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation);
+
+// Initialize alarms
+// Is called both on initialize new configs and config updates since alarms do not have any state.
+bool initAlarms(const StatsdConfig& config, const ConfigKey& key,
+ const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
+ const int64_t currentTimeNs, std::vector<sp<AlarmTracker>>& allAlarmTrackers);
+
+// Initialize MetricsManager from StatsdConfig.
+// Parameters are the members of MetricsManager. See MetricsManager for declaration.
+bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,
+ const sp<StatsPullerManager>& pullerManager,
+ const sp<AlarmMonitor>& anomalyAlarmMonitor,
+ const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
+ const int64_t currentTimeNs, std::set<int>& allTagIds,
+ std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ std::unordered_map<int64_t, int>& conditionTrackerMap,
+ std::vector<sp<MetricProducer>>& allMetricProducers,
+ std::unordered_map<int64_t, int>& metricProducerMap,
+ vector<sp<AnomalyTracker>>& allAnomalyTrackers,
+ vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::unordered_map<int64_t, int>& alertTrackerMap,
+ std::vector<int>& metricsWithActivation,
+ std::map<int64_t, uint64_t>& stateProtoHashes,
+ std::set<int64_t>& noReportMetricIds);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/bin/src/packages/PackageInfoListener.h b/bin/src/packages/PackageInfoListener.h
index 6c50a8c..1bc84c5 100644
--- a/bin/src/packages/PackageInfoListener.h
+++ b/bin/src/packages/PackageInfoListener.h
@@ -17,6 +17,8 @@
#ifndef STATSD_PACKAGE_INFO_LISTENER_H
#define STATSD_PACKAGE_INFO_LISTENER_H
+#include <utils/RefBase.h>
+
#include <string>
namespace android {
diff --git a/bin/src/packages/UidMap.h b/bin/src/packages/UidMap.h
index 22250ae..622321b 100644
--- a/bin/src/packages/UidMap.h
+++ b/bin/src/packages/UidMap.h
@@ -17,7 +17,6 @@
#pragma once
#include "config/ConfigKey.h"
-#include "config/ConfigListener.h"
#include "packages/PackageInfoListener.h"
#include "stats_util.h"
diff --git a/bin/src/shell/ShellSubscriber.cpp b/bin/src/shell/ShellSubscriber.cpp
index fd883c2..9d8f0c2 100644
--- a/bin/src/shell/ShellSubscriber.cpp
+++ b/bin/src/shell/ShellSubscriber.cpp
@@ -191,7 +191,7 @@
mProto.clear();
int count = 0;
for (const auto& event : data) {
- if (matchesSimple(*mUidMap, matcher, *event)) {
+ if (matchesSimple(mUidMap, matcher, *event)) {
count++;
uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
@@ -209,7 +209,7 @@
mProto.clear();
for (const auto& matcher : mSubscriptionInfo->mPushedMatchers) {
- if (matchesSimple(*mUidMap, matcher, event)) {
+ if (matchesSimple(mUidMap, matcher, event)) {
uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
event.ToProto(mProto);
diff --git a/bin/src/stats_log.proto b/bin/src/stats_log.proto
index bb07963..f91c4c0 100644
--- a/bin/src/stats_log.proto
+++ b/bin/src/stats_log.proto
@@ -488,6 +488,9 @@
optional int64 max_bucket_boundary_delay_ns = 10;
optional int64 bucket_unknown_condition = 11;
optional int64 bucket_count = 12;
+ optional int64 late_log_event = 13;
+ optional int64 sum_late_log_event_extra_duration_ns = 14;
+ optional int64 max_late_log_event_extra_duration_ns = 15;
}
repeated AtomMetricStats atom_metric_stats = 17;
diff --git a/bin/src/stats_log_util.cpp b/bin/src/stats_log_util.cpp
index 423bae8..d6e04f7 100644
--- a/bin/src/stats_log_util.cpp
+++ b/bin/src/stats_log_util.cpp
@@ -99,6 +99,9 @@
const int FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS = 10;
const int FIELD_ID_BUCKET_UNKNOWN_CONDITION = 11;
const int FIELD_ID_BUCKET_COUNT = 12;
+const int FIELD_ID_LATE_LOG_EVENT = 13;
+const int FIELD_ID_SUM_LATE_LOG_EVENT_EXTRA_DURATION_NS = 14;
+const int FIELD_ID_MAX_LATE_LOG_EVENT_EXTRA_DURATION_NS = 15;
namespace {
@@ -462,6 +465,13 @@
}
}
+void writeNonZeroStatToStream(const uint64_t fieldId, const int value,
+ util::ProtoOutputStream* protoOutput) {
+ if (value != 0) {
+ protoOutput->write(fieldId, value);
+ }
+}
+
void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats>& pair,
util::ProtoOutputStream* protoOutput) {
uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_PULLED_ATOM_STATS |
@@ -485,14 +495,12 @@
(long long)pair.second.pullTimeout);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_EXCEED_MAX_DELAY,
(long long)pair.second.pullExceedMaxDelay);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_FAILED,
- (long long)pair.second.pullFailed);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_EMPTY_DATA,
- (long long)pair.second.emptyData);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_FAILED, (long long)pair.second.pullFailed);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_EMPTY_DATA, (long long)pair.second.emptyData);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_REGISTERED_COUNT,
- (long long) pair.second.registeredCount);
+ (long long)pair.second.registeredCount);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_UNREGISTERED_COUNT,
- (long long) pair.second.unregisteredCount);
+ (long long)pair.second.unregisteredCount);
protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_ERROR_COUNT, pair.second.atomErrorCount);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BINDER_CALL_FAIL_COUNT,
(long long)pair.second.binderCallFailCount);
@@ -517,29 +525,37 @@
util::ProtoOutputStream *protoOutput) {
uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_METRIC_STATS |
FIELD_COUNT_REPEATED);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_ID, (long long)pair.first);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_HARD_DIMENSION_LIMIT_REACHED,
- (long long)pair.second.hardDimensionLimitReached);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_LATE_LOG_EVENT_SKIPPED,
- (long long)pair.second.lateLogEventSkipped);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_FORWARD_BUCKETS,
- (long long)pair.second.skippedForwardBuckets);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BAD_VALUE_TYPE,
- (long long)pair.second.badValueType);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_CHANGE_IN_NEXT_BUCKET,
- (long long)pair.second.conditionChangeInNextBucket);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_INVALIDATED_BUCKET,
- (long long)pair.second.invalidatedBucket);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_DROPPED,
- (long long)pair.second.bucketDropped);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MIN_BUCKET_BOUNDARY_DELAY_NS,
- (long long)pair.second.minBucketBoundaryDelayNs);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS,
- (long long)pair.second.maxBucketBoundaryDelayNs);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_UNKNOWN_CONDITION,
- (long long)pair.second.bucketUnknownCondition);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_COUNT,
- (long long)pair.second.bucketCount);
+
+ writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_METRIC_ID, (long long)pair.first,
+ protoOutput);
+ writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_HARD_DIMENSION_LIMIT_REACHED,
+ (long long)pair.second.hardDimensionLimitReached, protoOutput);
+ writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_LATE_LOG_EVENT_SKIPPED,
+ (long long)pair.second.lateLogEventSkipped, protoOutput);
+ writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_FORWARD_BUCKETS,
+ (long long)pair.second.skippedForwardBuckets, protoOutput);
+ writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_BAD_VALUE_TYPE,
+ (long long)pair.second.badValueType, protoOutput);
+ writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_CHANGE_IN_NEXT_BUCKET,
+ (long long)pair.second.conditionChangeInNextBucket, protoOutput);
+ writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_INVALIDATED_BUCKET,
+ (long long)pair.second.invalidatedBucket, protoOutput);
+ writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_DROPPED,
+ (long long)pair.second.bucketDropped, protoOutput);
+ writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_MIN_BUCKET_BOUNDARY_DELAY_NS,
+ (long long)pair.second.minBucketBoundaryDelayNs, protoOutput);
+ writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS,
+ (long long)pair.second.maxBucketBoundaryDelayNs, protoOutput);
+ writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_UNKNOWN_CONDITION,
+ (long long)pair.second.bucketUnknownCondition, protoOutput);
+ writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_COUNT,
+ (long long)pair.second.bucketCount, protoOutput);
+ writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_LATE_LOG_EVENT,
+ (long long)pair.second.lateLogEvent, protoOutput);
+ writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_SUM_LATE_LOG_EVENT_EXTRA_DURATION_NS,
+ (long long)pair.second.sumLateLogEventExtraDurationNs, protoOutput);
+ writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_MAX_LATE_LOG_EVENT_EXTRA_DURATION_NS,
+ (long long)pair.second.maxLateLogEventExtraDurationNs, protoOutput);
protoOutput->end(token);
}
diff --git a/bin/src/stats_log_util.h b/bin/src/stats_log_util.h
index 7fdde93..dcfe0c7 100644
--- a/bin/src/stats_log_util.h
+++ b/bin/src/stats_log_util.h
@@ -77,6 +77,10 @@
int64_t MillisToNano(const int64_t millis);
+// Helper function to write a stats field to ProtoOutputStream if it's a non-zero value.
+void writeNonZeroStatToStream(const uint64_t fieldId, const int value,
+ ProtoOutputStream* protoOutput);
+
// Helper function to write PulledAtomStats to ProtoOutputStream
void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats>& pair,
ProtoOutputStream* protoOutput);
diff --git a/bin/tests/ConfigManager_test.cpp b/bin/tests/ConfigManager_test.cpp
index 9455304..24c8f2b 100644
--- a/bin/tests/ConfigManager_test.cpp
+++ b/bin/tests/ConfigManager_test.cpp
@@ -44,8 +44,8 @@
*/
class MockListener : public ConfigListener {
public:
- MOCK_METHOD3(OnConfigUpdated, void(const int64_t timestampNs, const ConfigKey& key,
- const StatsdConfig& config));
+ MOCK_METHOD3(OnConfigUpdated,
+ void(const int64_t timestampNs, const ConfigKey& key, const StatsdConfig& config));
MOCK_METHOD1(OnConfigRemoved, void(const ConfigKey& key));
};
@@ -89,26 +89,26 @@
manager->StartupForTest();
// Add another one
- EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, ConfigKeyEq(1, StringToId("zzz")),
- StatsdConfigEq(91)))
+ EXPECT_CALL(*(listener.get()),
+ OnConfigUpdated(_, ConfigKeyEq(1, StringToId("zzz")), StatsdConfigEq(91)))
.RetiresOnSaturation();
manager->UpdateConfig(ConfigKey(1, StringToId("zzz")), config91);
// Update It
- EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, ConfigKeyEq(1, StringToId("zzz")),
- StatsdConfigEq(92)))
+ EXPECT_CALL(*(listener.get()),
+ OnConfigUpdated(_, ConfigKeyEq(1, StringToId("zzz")), StatsdConfigEq(92)))
.RetiresOnSaturation();
manager->UpdateConfig(ConfigKey(1, StringToId("zzz")), config92);
// Add one with the same uid but a different name
- EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, ConfigKeyEq(1, StringToId("yyy")),
- StatsdConfigEq(93)))
+ EXPECT_CALL(*(listener.get()),
+ OnConfigUpdated(_, ConfigKeyEq(1, StringToId("yyy")), StatsdConfigEq(93)))
.RetiresOnSaturation();
manager->UpdateConfig(ConfigKey(1, StringToId("yyy")), config93);
// Add one with the same name but a different uid
- EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, ConfigKeyEq(2, StringToId("zzz")),
- StatsdConfigEq(94)))
+ EXPECT_CALL(*(listener.get()),
+ OnConfigUpdated(_, ConfigKeyEq(2, StringToId("zzz")), StatsdConfigEq(94)))
.RetiresOnSaturation();
manager->UpdateConfig(ConfigKey(2, StringToId("zzz")), config94);
diff --git a/bin/tests/LogEntryMatcher_test.cpp b/bin/tests/LogEntryMatcher_test.cpp
index 00fa55f..6a433d4 100644
--- a/bin/tests/LogEntryMatcher_test.cpp
+++ b/bin/tests/LogEntryMatcher_test.cpp
@@ -110,7 +110,7 @@
} // anonymous namespace
TEST(AtomMatcherTest, TestSimpleMatcher) {
- UidMap uidMap;
+ sp<UidMap> uidMap = new UidMap();
// Set up the matcher
AtomMatcher matcher;
@@ -129,7 +129,7 @@
}
TEST(AtomMatcherTest, TestAttributionMatcher) {
- UidMap uidMap;
+ sp<UidMap> uidMap = new UidMap();
std::vector<int> attributionUids = {1111, 2222, 3333};
std::vector<string> attributionTags = {"location1", "location2", "location3"};
@@ -204,7 +204,7 @@
"pkg0");
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- uidMap.updateMap(
+ uidMap->updateMap(
1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
{android::String16("v1"), android::String16("v1"), android::String16("v2"),
android::String16("v1"), android::String16("v2")},
@@ -356,8 +356,8 @@
}
TEST(AtomMatcherTest, TestUidFieldMatcher) {
- UidMap uidMap;
- uidMap.updateMap(
+ sp<UidMap> uidMap = new UidMap();
+ uidMap->updateMap(
1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
{android::String16("v1"), android::String16("v1"), android::String16("v2"),
android::String16("v1"), android::String16("v2")},
@@ -392,8 +392,8 @@
}
TEST(AtomMatcherTest, TestNeqAnyStringMatcher) {
- UidMap uidMap;
- uidMap.updateMap(
+ sp<UidMap> uidMap = new UidMap();
+ uidMap->updateMap(
1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
{android::String16("v1"), android::String16("v1"), android::String16("v2"),
android::String16("v1"), android::String16("v2")},
@@ -453,8 +453,8 @@
}
TEST(AtomMatcherTest, TestEqAnyStringMatcher) {
- UidMap uidMap;
- uidMap.updateMap(
+ sp<UidMap> uidMap = new UidMap();
+ uidMap->updateMap(
1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
{android::String16("v1"), android::String16("v1"), android::String16("v2"),
android::String16("v1"), android::String16("v2")},
@@ -517,7 +517,7 @@
}
TEST(AtomMatcherTest, TestBoolMatcher) {
- UidMap uidMap;
+ sp<UidMap> uidMap = new UidMap();
// Set up the matcher
AtomMatcher matcher;
auto simpleMatcher = matcher.mutable_simple_atom_matcher();
@@ -550,7 +550,7 @@
}
TEST(AtomMatcherTest, TestStringMatcher) {
- UidMap uidMap;
+ sp<UidMap> uidMap = new UidMap();
// Set up the matcher
AtomMatcher matcher;
auto simpleMatcher = matcher.mutable_simple_atom_matcher();
@@ -568,7 +568,7 @@
}
TEST(AtomMatcherTest, TestMultiFieldsMatcher) {
- UidMap uidMap;
+ sp<UidMap> uidMap = new UidMap();
// Set up the matcher
AtomMatcher matcher;
auto simpleMatcher = matcher.mutable_simple_atom_matcher();
@@ -597,7 +597,7 @@
}
TEST(AtomMatcherTest, TestIntComparisonMatcher) {
- UidMap uidMap;
+ sp<UidMap> uidMap = new UidMap();
// Set up the matcher
AtomMatcher matcher;
auto simpleMatcher = matcher.mutable_simple_atom_matcher();
@@ -654,7 +654,7 @@
}
TEST(AtomMatcherTest, TestFloatComparisonMatcher) {
- UidMap uidMap;
+ sp<UidMap> uidMap = new UidMap();
// Set up the matcher
AtomMatcher matcher;
auto simpleMatcher = matcher.mutable_simple_atom_matcher();
diff --git a/bin/tests/MetricsManager_test.cpp b/bin/tests/MetricsManager_test.cpp
index aa562f1..62c9e2e 100644
--- a/bin/tests/MetricsManager_test.cpp
+++ b/bin/tests/MetricsManager_test.cpp
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <private/android_filesystem_config.h>
#include <stdio.h>
@@ -23,12 +24,12 @@
#include "packages/modules/StatsD/bin/src/statsd_config.pb.h"
#include "metrics/metrics_test_helper.h"
#include "src/condition/ConditionTracker.h"
-#include "src/matchers/LogMatchingTracker.h"
+#include "src/matchers/AtomMatchingTracker.h"
#include "src/metrics/CountMetricProducer.h"
#include "src/metrics/GaugeMetricProducer.h"
#include "src/metrics/MetricProducer.h"
#include "src/metrics/ValueMetricProducer.h"
-#include "src/metrics/metrics_manager_util.h"
+#include "src/metrics/parsing_utils/metrics_manager_util.h"
#include "src/state/StateManager.h"
#include "statsd_test_util.h"
@@ -48,7 +49,6 @@
namespace {
const ConfigKey kConfigKey(0, 12345);
-const long kAlertId = 3;
const long timeBaseSec = 1000;
@@ -90,563 +90,18 @@
metric->set_bucket(ONE_MINUTE);
metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/);
metric->mutable_dimensions_in_what()->add_child()->set_field(1);
-
- config.add_no_report_metric(3);
-
- auto alert = config.add_alert();
- alert->set_id(kAlertId);
- alert->set_metric_id(3);
- alert->set_num_buckets(10);
- alert->set_refractory_period_secs(100);
- alert->set_trigger_if_sum_gt(100);
return config;
}
-StatsdConfig buildCircleMatchers() {
- StatsdConfig config;
- config.set_id(12345);
-
- AtomMatcher* eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
-
- SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
- simpleAtomMatcher->add_field_value_matcher()->set_field(
- 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
- 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
-
- eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
-
- AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
- combination->set_operation(LogicalOperation::OR);
- combination->add_matcher(StringToId("SCREEN_IS_ON"));
- // Circle dependency
- combination->add_matcher(StringToId("SCREEN_ON_OR_OFF"));
-
- return config;
-}
-
-StatsdConfig buildAlertWithUnknownMetric() {
- StatsdConfig config;
- config.set_id(12345);
-
- AtomMatcher* eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
-
- CountMetric* metric = config.add_count_metric();
- metric->set_id(3);
- metric->set_what(StringToId("SCREEN_IS_ON"));
- metric->set_bucket(ONE_MINUTE);
- metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/);
- metric->mutable_dimensions_in_what()->add_child()->set_field(1);
-
- auto alert = config.add_alert();
- alert->set_id(3);
- alert->set_metric_id(2);
- alert->set_num_buckets(10);
- alert->set_refractory_period_secs(100);
- alert->set_trigger_if_sum_gt(100);
- return config;
-}
-
-StatsdConfig buildMissingMatchers() {
- StatsdConfig config;
- config.set_id(12345);
-
- AtomMatcher* eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
-
- SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
- simpleAtomMatcher->add_field_value_matcher()->set_field(
- 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
- 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
-
- eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
-
- AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
- combination->set_operation(LogicalOperation::OR);
- combination->add_matcher(StringToId("SCREEN_IS_ON"));
- // undefined matcher
- combination->add_matcher(StringToId("ABC"));
-
- return config;
-}
-
-StatsdConfig buildMissingPredicate() {
- StatsdConfig config;
- config.set_id(12345);
-
- CountMetric* metric = config.add_count_metric();
- metric->set_id(3);
- metric->set_what(StringToId("SCREEN_EVENT"));
- metric->set_bucket(ONE_MINUTE);
- metric->set_condition(StringToId("SOME_CONDITION"));
-
- AtomMatcher* eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_EVENT"));
-
- SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_atom_id(2);
-
- return config;
-}
-
-StatsdConfig buildDimensionMetricsWithMultiTags() {
- StatsdConfig config;
- config.set_id(12345);
-
- AtomMatcher* eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("BATTERY_VERY_LOW"));
- SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_atom_id(2);
-
- eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("BATTERY_VERY_VERY_LOW"));
- simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_atom_id(3);
-
- eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("BATTERY_LOW"));
-
- AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
- combination->set_operation(LogicalOperation::OR);
- combination->add_matcher(StringToId("BATTERY_VERY_LOW"));
- combination->add_matcher(StringToId("BATTERY_VERY_VERY_LOW"));
-
- // Count process state changes, slice by uid, while SCREEN_IS_OFF
- CountMetric* metric = config.add_count_metric();
- metric->set_id(3);
- metric->set_what(StringToId("BATTERY_LOW"));
- metric->set_bucket(ONE_MINUTE);
- // This case is interesting. We want to dimension across two atoms.
- metric->mutable_dimensions_in_what()->add_child()->set_field(1);
-
- auto alert = config.add_alert();
- alert->set_id(kAlertId);
- alert->set_metric_id(3);
- alert->set_num_buckets(10);
- alert->set_refractory_period_secs(100);
- alert->set_trigger_if_sum_gt(100);
- return config;
-}
-
-StatsdConfig buildCirclePredicates() {
- StatsdConfig config;
- config.set_id(12345);
-
- AtomMatcher* eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
-
- SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
- simpleAtomMatcher->add_field_value_matcher()->set_field(
- 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
- 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
-
- eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_IS_OFF"));
-
- simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
- simpleAtomMatcher->add_field_value_matcher()->set_field(
- 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
- 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
-
- auto condition = config.add_predicate();
- condition->set_id(StringToId("SCREEN_IS_ON"));
- SimplePredicate* simplePredicate = condition->mutable_simple_predicate();
- simplePredicate->set_start(StringToId("SCREEN_IS_ON"));
- simplePredicate->set_stop(StringToId("SCREEN_IS_OFF"));
-
- condition = config.add_predicate();
- condition->set_id(StringToId("SCREEN_IS_EITHER_ON_OFF"));
-
- Predicate_Combination* combination = condition->mutable_combination();
- combination->set_operation(LogicalOperation::OR);
- combination->add_predicate(StringToId("SCREEN_IS_ON"));
- combination->add_predicate(StringToId("SCREEN_IS_EITHER_ON_OFF"));
-
- return config;
-}
-
-StatsdConfig buildConfigWithDifferentPredicates() {
- StatsdConfig config;
- config.set_id(12345);
-
- auto pulledAtomMatcher =
- CreateSimpleAtomMatcher("SUBSYSTEM_SLEEP", util::SUBSYSTEM_SLEEP_STATE);
- *config.add_atom_matcher() = pulledAtomMatcher;
- auto screenOnAtomMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = screenOnAtomMatcher;
- auto screenOffAtomMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = screenOffAtomMatcher;
- auto batteryNoneAtomMatcher = CreateBatteryStateNoneMatcher();
- *config.add_atom_matcher() = batteryNoneAtomMatcher;
- auto batteryUsbAtomMatcher = CreateBatteryStateUsbMatcher();
- *config.add_atom_matcher() = batteryUsbAtomMatcher;
-
- // Simple condition with InitialValue set to default (unknown).
- auto screenOnUnknownPredicate = CreateScreenIsOnPredicate();
- *config.add_predicate() = screenOnUnknownPredicate;
-
- // Simple condition with InitialValue set to false.
- auto screenOnFalsePredicate = config.add_predicate();
- screenOnFalsePredicate->set_id(StringToId("ScreenIsOnInitialFalse"));
- SimplePredicate* simpleScreenOnFalsePredicate =
- screenOnFalsePredicate->mutable_simple_predicate();
- simpleScreenOnFalsePredicate->set_start(screenOnAtomMatcher.id());
- simpleScreenOnFalsePredicate->set_stop(screenOffAtomMatcher.id());
- simpleScreenOnFalsePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE);
-
- // Simple condition with InitialValue set to false.
- auto onBatteryFalsePredicate = config.add_predicate();
- onBatteryFalsePredicate->set_id(StringToId("OnBatteryInitialFalse"));
- SimplePredicate* simpleOnBatteryFalsePredicate =
- onBatteryFalsePredicate->mutable_simple_predicate();
- simpleOnBatteryFalsePredicate->set_start(batteryNoneAtomMatcher.id());
- simpleOnBatteryFalsePredicate->set_stop(batteryUsbAtomMatcher.id());
- simpleOnBatteryFalsePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE);
-
- // Combination condition with both simple condition InitialValues set to false.
- auto screenOnFalseOnBatteryFalsePredicate = config.add_predicate();
- screenOnFalseOnBatteryFalsePredicate->set_id(StringToId("ScreenOnFalseOnBatteryFalse"));
- screenOnFalseOnBatteryFalsePredicate->mutable_combination()->set_operation(
- LogicalOperation::AND);
- addPredicateToPredicateCombination(*screenOnFalsePredicate,
- screenOnFalseOnBatteryFalsePredicate);
- addPredicateToPredicateCombination(*onBatteryFalsePredicate,
- screenOnFalseOnBatteryFalsePredicate);
-
- // Combination condition with one simple condition InitialValue set to unknown and one set to
- // false.
- auto screenOnUnknownOnBatteryFalsePredicate = config.add_predicate();
- screenOnUnknownOnBatteryFalsePredicate->set_id(StringToId("ScreenOnUnknowneOnBatteryFalse"));
- screenOnUnknownOnBatteryFalsePredicate->mutable_combination()->set_operation(
- LogicalOperation::AND);
- addPredicateToPredicateCombination(screenOnUnknownPredicate,
- screenOnUnknownOnBatteryFalsePredicate);
- addPredicateToPredicateCombination(*onBatteryFalsePredicate,
- screenOnUnknownOnBatteryFalsePredicate);
-
- // Simple condition metric with initial value false.
- ValueMetric* metric1 = config.add_value_metric();
- metric1->set_id(StringToId("ValueSubsystemSleepWhileScreenOnInitialFalse"));
- metric1->set_what(pulledAtomMatcher.id());
- *metric1->mutable_value_field() =
- CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
- metric1->set_bucket(FIVE_MINUTES);
- metric1->set_condition(screenOnFalsePredicate->id());
-
- // Simple condition metric with initial value unknown.
- ValueMetric* metric2 = config.add_value_metric();
- metric2->set_id(StringToId("ValueSubsystemSleepWhileScreenOnInitialUnknown"));
- metric2->set_what(pulledAtomMatcher.id());
- *metric2->mutable_value_field() =
- CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
- metric2->set_bucket(FIVE_MINUTES);
- metric2->set_condition(screenOnUnknownPredicate.id());
-
- // Combination condition metric with initial values false and false.
- ValueMetric* metric3 = config.add_value_metric();
- metric3->set_id(StringToId("ValueSubsystemSleepWhileScreenOnFalseDeviceUnpluggedFalse"));
- metric3->set_what(pulledAtomMatcher.id());
- *metric3->mutable_value_field() =
- CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
- metric3->set_bucket(FIVE_MINUTES);
- metric3->set_condition(screenOnFalseOnBatteryFalsePredicate->id());
-
- // Combination condition metric with initial values unknown and false.
- ValueMetric* metric4 = config.add_value_metric();
- metric4->set_id(StringToId("ValueSubsystemSleepWhileScreenOnUnknownDeviceUnpluggedFalse"));
- metric4->set_what(pulledAtomMatcher.id());
- *metric4->mutable_value_field() =
- CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
- metric4->set_bucket(FIVE_MINUTES);
- metric4->set_condition(screenOnUnknownOnBatteryFalsePredicate->id());
-
- return config;
-}
-
-bool isSubset(const set<int32_t>& set1, const set<int32_t>& set2) {
- return std::includes(set2.begin(), set2.end(), set1.begin(), set1.end());
+set<int32_t> unionSet(const vector<set<int32_t>> sets) {
+ set<int32_t> toRet;
+ for (const set<int32_t>& s : sets) {
+ toRet.insert(s.begin(), s.end());
+ }
+ return toRet;
}
} // anonymous namespace
-TEST(MetricsManagerTest, TestInitialConditions) {
- UidMap uidMap;
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> periodicAlarmMonitor;
- StatsdConfig config = buildConfigWithDifferentPredicates();
- set<int> allTagIds;
- vector<sp<LogMatchingTracker>> allAtomMatchers;
- vector<sp<ConditionTracker>> allConditionTrackers;
- vector<sp<MetricProducer>> allMetricProducers;
- std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
- std::vector<sp<AlarmTracker>> allAlarmTrackers;
- unordered_map<int, std::vector<int>> conditionToMetricMap;
- unordered_map<int, std::vector<int>> trackerToMetricMap;
- unordered_map<int, std::vector<int>> trackerToConditionMap;
- unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
- unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
- unordered_map<int64_t, int> alertTrackerMap;
- vector<int> metricsWithActivation;
- std::set<int64_t> noReportMetricIds;
-
- EXPECT_TRUE(initStatsdConfig(
- kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers,
- allMetricProducers, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
- trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
- noReportMetricIds));
- ASSERT_EQ(4u, allMetricProducers.size());
- ASSERT_EQ(5u, allConditionTrackers.size());
-
- ConditionKey queryKey;
- vector<ConditionState> conditionCache(5, ConditionState::kNotEvaluated);
-
- allConditionTrackers[3]->isConditionMet(queryKey, allConditionTrackers, false, conditionCache);
- allConditionTrackers[4]->isConditionMet(queryKey, allConditionTrackers, false, conditionCache);
- EXPECT_EQ(ConditionState::kUnknown, conditionCache[0]);
- EXPECT_EQ(ConditionState::kFalse, conditionCache[1]);
- EXPECT_EQ(ConditionState::kFalse, conditionCache[2]);
- EXPECT_EQ(ConditionState::kFalse, conditionCache[3]);
- EXPECT_EQ(ConditionState::kUnknown, conditionCache[4]);
-
- EXPECT_EQ(ConditionState::kFalse, allMetricProducers[0]->mCondition);
- EXPECT_EQ(ConditionState::kUnknown, allMetricProducers[1]->mCondition);
- EXPECT_EQ(ConditionState::kFalse, allMetricProducers[2]->mCondition);
- EXPECT_EQ(ConditionState::kUnknown, allMetricProducers[3]->mCondition);
-}
-
-TEST(MetricsManagerTest, TestGoodConfig) {
- UidMap uidMap;
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> periodicAlarmMonitor;
- StatsdConfig config = buildGoodConfig();
- set<int> allTagIds;
- vector<sp<LogMatchingTracker>> allAtomMatchers;
- vector<sp<ConditionTracker>> allConditionTrackers;
- vector<sp<MetricProducer>> allMetricProducers;
- std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
- std::vector<sp<AlarmTracker>> allAlarmTrackers;
- unordered_map<int, std::vector<int>> conditionToMetricMap;
- unordered_map<int, std::vector<int>> trackerToMetricMap;
- unordered_map<int, std::vector<int>> trackerToConditionMap;
- unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
- unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
- unordered_map<int64_t, int> alertTrackerMap;
- vector<int> metricsWithActivation;
- std::set<int64_t> noReportMetricIds;
-
- EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
- periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
- allAtomMatchers, allConditionTrackers, allMetricProducers,
- allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
- trackerToMetricMap, trackerToConditionMap,
- activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- alertTrackerMap, metricsWithActivation,
- noReportMetricIds));
- ASSERT_EQ(1u, allMetricProducers.size());
- ASSERT_EQ(1u, allAnomalyTrackers.size());
- ASSERT_EQ(1u, noReportMetricIds.size());
- ASSERT_EQ(1u, alertTrackerMap.size());
- EXPECT_NE(alertTrackerMap.find(kAlertId), alertTrackerMap.end());
- EXPECT_EQ(alertTrackerMap.find(kAlertId)->second, 0);
-}
-
-TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) {
- UidMap uidMap;
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> periodicAlarmMonitor;
- StatsdConfig config = buildDimensionMetricsWithMultiTags();
- set<int> allTagIds;
- vector<sp<LogMatchingTracker>> allAtomMatchers;
- vector<sp<ConditionTracker>> allConditionTrackers;
- vector<sp<MetricProducer>> allMetricProducers;
- std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
- std::vector<sp<AlarmTracker>> allAlarmTrackers;
- unordered_map<int, std::vector<int>> conditionToMetricMap;
- unordered_map<int, std::vector<int>> trackerToMetricMap;
- unordered_map<int, std::vector<int>> trackerToConditionMap;
- unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
- unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
- unordered_map<int64_t, int> alertTrackerMap;
- vector<int> metricsWithActivation;
- std::set<int64_t> noReportMetricIds;
-
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
- periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
- allAtomMatchers, allConditionTrackers, allMetricProducers,
- allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
- trackerToMetricMap, trackerToConditionMap,
- activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- alertTrackerMap, metricsWithActivation,
- noReportMetricIds));
-}
-
-TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
- UidMap uidMap;
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> periodicAlarmMonitor;
- StatsdConfig config = buildCircleMatchers();
- set<int> allTagIds;
- vector<sp<LogMatchingTracker>> allAtomMatchers;
- vector<sp<ConditionTracker>> allConditionTrackers;
- vector<sp<MetricProducer>> allMetricProducers;
- std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
- std::vector<sp<AlarmTracker>> allAlarmTrackers;
- unordered_map<int, std::vector<int>> conditionToMetricMap;
- unordered_map<int, std::vector<int>> trackerToMetricMap;
- unordered_map<int, std::vector<int>> trackerToConditionMap;
- unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
- unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
- unordered_map<int64_t, int> alertTrackerMap;
- vector<int> metricsWithActivation;
- std::set<int64_t> noReportMetricIds;
-
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
- periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
- allAtomMatchers, allConditionTrackers, allMetricProducers,
- allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
- trackerToMetricMap, trackerToConditionMap,
- activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- alertTrackerMap, metricsWithActivation,
- noReportMetricIds));
-}
-
-TEST(MetricsManagerTest, TestMissingMatchers) {
- UidMap uidMap;
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> periodicAlarmMonitor;
- StatsdConfig config = buildMissingMatchers();
- set<int> allTagIds;
- vector<sp<LogMatchingTracker>> allAtomMatchers;
- vector<sp<ConditionTracker>> allConditionTrackers;
- vector<sp<MetricProducer>> allMetricProducers;
- std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
- std::vector<sp<AlarmTracker>> allAlarmTrackers;
- unordered_map<int, std::vector<int>> conditionToMetricMap;
- unordered_map<int, std::vector<int>> trackerToMetricMap;
- unordered_map<int, std::vector<int>> trackerToConditionMap;
- unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
- unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
- unordered_map<int64_t, int> alertTrackerMap;
- vector<int> metricsWithActivation;
- std::set<int64_t> noReportMetricIds;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
- periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
- allAtomMatchers, allConditionTrackers, allMetricProducers,
- allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
- trackerToMetricMap, trackerToConditionMap,
- activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- alertTrackerMap, metricsWithActivation,
- noReportMetricIds));
-}
-
-TEST(MetricsManagerTest, TestMissingPredicate) {
- UidMap uidMap;
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> periodicAlarmMonitor;
- StatsdConfig config = buildMissingPredicate();
- set<int> allTagIds;
- vector<sp<LogMatchingTracker>> allAtomMatchers;
- vector<sp<ConditionTracker>> allConditionTrackers;
- vector<sp<MetricProducer>> allMetricProducers;
- std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
- std::vector<sp<AlarmTracker>> allAlarmTrackers;
- unordered_map<int, std::vector<int>> conditionToMetricMap;
- unordered_map<int, std::vector<int>> trackerToMetricMap;
- unordered_map<int, std::vector<int>> trackerToConditionMap;
- unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
- unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
- unordered_map<int64_t, int> alertTrackerMap;
- vector<int> metricsWithActivation;
- std::set<int64_t> noReportMetricIds;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
- periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
- allAtomMatchers, allConditionTrackers, allMetricProducers,
- allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
- trackerToMetricMap, trackerToConditionMap,
- activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- alertTrackerMap, metricsWithActivation, noReportMetricIds));
-}
-
-TEST(MetricsManagerTest, TestCirclePredicateDependency) {
- UidMap uidMap;
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> periodicAlarmMonitor;
- StatsdConfig config = buildCirclePredicates();
- set<int> allTagIds;
- vector<sp<LogMatchingTracker>> allAtomMatchers;
- vector<sp<ConditionTracker>> allConditionTrackers;
- vector<sp<MetricProducer>> allMetricProducers;
- std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
- std::vector<sp<AlarmTracker>> allAlarmTrackers;
- unordered_map<int, std::vector<int>> conditionToMetricMap;
- unordered_map<int, std::vector<int>> trackerToMetricMap;
- unordered_map<int, std::vector<int>> trackerToConditionMap;
- unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
- unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
- unordered_map<int64_t, int> alertTrackerMap;
- vector<int> metricsWithActivation;
- std::set<int64_t> noReportMetricIds;
-
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
- periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
- allAtomMatchers, allConditionTrackers, allMetricProducers,
- allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
- trackerToMetricMap, trackerToConditionMap,
- activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- alertTrackerMap, metricsWithActivation,
- noReportMetricIds));
-}
-
-TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
- UidMap uidMap;
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> periodicAlarmMonitor;
- StatsdConfig config = buildAlertWithUnknownMetric();
- set<int> allTagIds;
- vector<sp<LogMatchingTracker>> allAtomMatchers;
- vector<sp<ConditionTracker>> allConditionTrackers;
- vector<sp<MetricProducer>> allMetricProducers;
- std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
- std::vector<sp<AlarmTracker>> allAlarmTrackers;
- unordered_map<int, std::vector<int>> conditionToMetricMap;
- unordered_map<int, std::vector<int>> trackerToMetricMap;
- unordered_map<int, std::vector<int>> trackerToConditionMap;
- unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
- unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
- unordered_map<int64_t, int> alertTrackerMap;
- vector<int> metricsWithActivation;
- std::set<int64_t> noReportMetricIds;
-
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
- periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
- allAtomMatchers, allConditionTrackers, allMetricProducers,
- allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
- trackerToMetricMap, trackerToConditionMap,
- activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- alertTrackerMap, metricsWithActivation,
- noReportMetricIds));
-}
-
TEST(MetricsManagerTest, TestLogSources) {
string app1 = "app1";
set<int32_t> app1Uids = {1111, 11111};
@@ -660,9 +115,7 @@
pkgToUids[app2] = app2Uids;
pkgToUids[app3] = app3Uids;
- int32_t atom1 = 10;
- int32_t atom2 = 20;
- int32_t atom3 = 30;
+ int32_t atom1 = 10, atom2 = 20, atom3 = 30;
sp<MockUidMap> uidMap = new StrictMock<MockUidMap>();
EXPECT_CALL(*uidMap, getAppUid(_))
.Times(4)
@@ -680,7 +133,7 @@
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> periodicAlarmMonitor;
- StatsdConfig config = buildGoodConfig();
+ StatsdConfig config;
config.add_allowed_log_source("AID_SYSTEM");
config.add_allowed_log_source(app1);
config.add_default_pull_packages("AID_SYSTEM");
@@ -700,42 +153,115 @@
MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap,
pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor);
-
EXPECT_TRUE(metricsManager.isConfigValid());
- ASSERT_EQ(metricsManager.mAllowedUid.size(), 1);
- EXPECT_EQ(metricsManager.mAllowedUid[0], AID_SYSTEM);
-
- ASSERT_EQ(metricsManager.mAllowedPkg.size(), 1);
- EXPECT_EQ(metricsManager.mAllowedPkg[0], app1);
-
- ASSERT_EQ(metricsManager.mAllowedLogSources.size(), 3);
- EXPECT_TRUE(isSubset({AID_SYSTEM}, metricsManager.mAllowedLogSources));
- EXPECT_TRUE(isSubset(app1Uids, metricsManager.mAllowedLogSources));
-
- ASSERT_EQ(metricsManager.mDefaultPullUids.size(), 2);
- EXPECT_TRUE(isSubset(defaultPullUids, metricsManager.mDefaultPullUids));
- ;
+ EXPECT_THAT(metricsManager.mAllowedUid, ElementsAre(AID_SYSTEM));
+ EXPECT_THAT(metricsManager.mAllowedPkg, ElementsAre(app1));
+ EXPECT_THAT(metricsManager.mAllowedLogSources,
+ ContainerEq(unionSet(vector<set<int32_t>>({app1Uids, {AID_SYSTEM}}))));
+ EXPECT_THAT(metricsManager.mDefaultPullUids, ContainerEq(defaultPullUids));
vector<int32_t> atom1Uids = metricsManager.getPullAtomUids(atom1);
- ASSERT_EQ(atom1Uids.size(), 5);
- set<int32_t> expectedAtom1Uids;
- expectedAtom1Uids.insert(defaultPullUids.begin(), defaultPullUids.end());
- expectedAtom1Uids.insert(app1Uids.begin(), app1Uids.end());
- expectedAtom1Uids.insert(app3Uids.begin(), app3Uids.end());
- EXPECT_TRUE(isSubset(expectedAtom1Uids, set<int32_t>(atom1Uids.begin(), atom1Uids.end())));
+ EXPECT_THAT(atom1Uids,
+ UnorderedElementsAreArray(unionSet({defaultPullUids, app1Uids, app3Uids})));
vector<int32_t> atom2Uids = metricsManager.getPullAtomUids(atom2);
- ASSERT_EQ(atom2Uids.size(), 4);
- set<int32_t> expectedAtom2Uids;
- expectedAtom1Uids.insert(defaultPullUids.begin(), defaultPullUids.end());
- expectedAtom1Uids.insert(app2Uids.begin(), app2Uids.end());
- expectedAtom1Uids.insert(AID_STATSD);
- EXPECT_TRUE(isSubset(expectedAtom2Uids, set<int32_t>(atom2Uids.begin(), atom2Uids.end())));
+ EXPECT_THAT(atom2Uids,
+ UnorderedElementsAreArray(unionSet({defaultPullUids, app2Uids, {AID_STATSD}})));
vector<int32_t> atom3Uids = metricsManager.getPullAtomUids(atom3);
- ASSERT_EQ(atom3Uids.size(), 2);
- EXPECT_TRUE(isSubset(defaultPullUids, set<int32_t>(atom3Uids.begin(), atom3Uids.end())));
+ EXPECT_THAT(atom3Uids, UnorderedElementsAreArray(defaultPullUids));
+}
+
+TEST(MetricsManagerTest, TestLogSourcesOnConfigUpdate) {
+ string app1 = "app1";
+ set<int32_t> app1Uids = {1111, 11111};
+ string app2 = "app2";
+ set<int32_t> app2Uids = {2222};
+ string app3 = "app3";
+ set<int32_t> app3Uids = {3333, 1111};
+
+ map<string, set<int32_t>> pkgToUids;
+ pkgToUids[app1] = app1Uids;
+ pkgToUids[app2] = app2Uids;
+ pkgToUids[app3] = app3Uids;
+
+ int32_t atom1 = 10, atom2 = 20, atom3 = 30;
+ sp<MockUidMap> uidMap = new StrictMock<MockUidMap>();
+ EXPECT_CALL(*uidMap, getAppUid(_))
+ .Times(8)
+ .WillRepeatedly(Invoke([&pkgToUids](const string& pkg) {
+ const auto& it = pkgToUids.find(pkg);
+ if (it != pkgToUids.end()) {
+ return it->second;
+ }
+ return set<int32_t>();
+ }));
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, RegisterPullUidProvider(kConfigKey, _)).Times(1);
+ EXPECT_CALL(*pullerManager, UnregisterPullUidProvider(kConfigKey, _)).Times(1);
+
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
+
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_SYSTEM");
+ config.add_allowed_log_source(app1);
+ config.add_default_pull_packages("AID_SYSTEM");
+ config.add_default_pull_packages("AID_ROOT");
+
+ PullAtomPackages* pullAtomPackages = config.add_pull_atom_packages();
+ pullAtomPackages->set_atom_id(atom1);
+ pullAtomPackages->add_packages(app1);
+ pullAtomPackages->add_packages(app3);
+
+ pullAtomPackages = config.add_pull_atom_packages();
+ pullAtomPackages->set_atom_id(atom2);
+ pullAtomPackages->add_packages(app2);
+ pullAtomPackages->add_packages("AID_STATSD");
+
+ MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap,
+ pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor);
+ EXPECT_TRUE(metricsManager.isConfigValid());
+
+ // Update with new allowed log sources.
+ StatsdConfig newConfig;
+ newConfig.add_allowed_log_source("AID_ROOT");
+ newConfig.add_allowed_log_source(app2);
+ newConfig.add_default_pull_packages("AID_SYSTEM");
+ newConfig.add_default_pull_packages("AID_STATSD");
+
+ pullAtomPackages = newConfig.add_pull_atom_packages();
+ pullAtomPackages->set_atom_id(atom2);
+ pullAtomPackages->add_packages(app1);
+ pullAtomPackages->add_packages(app3);
+
+ pullAtomPackages = newConfig.add_pull_atom_packages();
+ pullAtomPackages->set_atom_id(atom3);
+ pullAtomPackages->add_packages(app2);
+ pullAtomPackages->add_packages("AID_ADB");
+
+ metricsManager.updateConfig(newConfig, timeBaseSec, timeBaseSec, anomalyAlarmMonitor,
+ periodicAlarmMonitor);
+ EXPECT_TRUE(metricsManager.isConfigValid());
+
+ EXPECT_THAT(metricsManager.mAllowedUid, ElementsAre(AID_ROOT));
+ EXPECT_THAT(metricsManager.mAllowedPkg, ElementsAre(app2));
+ EXPECT_THAT(metricsManager.mAllowedLogSources,
+ ContainerEq(unionSet(vector<set<int32_t>>({app2Uids, {AID_ROOT}}))));
+ const set<int32_t> defaultPullUids = {AID_SYSTEM, AID_STATSD};
+ EXPECT_THAT(metricsManager.mDefaultPullUids, ContainerEq(defaultPullUids));
+
+ vector<int32_t> atom1Uids = metricsManager.getPullAtomUids(atom1);
+ EXPECT_THAT(atom1Uids, UnorderedElementsAreArray(defaultPullUids));
+
+ vector<int32_t> atom2Uids = metricsManager.getPullAtomUids(atom2);
+ EXPECT_THAT(atom2Uids,
+ UnorderedElementsAreArray(unionSet({defaultPullUids, app1Uids, app3Uids})));
+
+ vector<int32_t> atom3Uids = metricsManager.getPullAtomUids(atom3);
+ EXPECT_THAT(atom3Uids,
+ UnorderedElementsAreArray(unionSet({defaultPullUids, app2Uids, {AID_ADB}})));
}
TEST(MetricsManagerTest, TestCheckLogCredentialsWhitelistedAtom) {
@@ -744,7 +270,7 @@
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> periodicAlarmMonitor;
- StatsdConfig config = buildGoodConfig();
+ StatsdConfig config;
config.add_whitelisted_atom_ids(3);
config.add_whitelisted_atom_ids(4);
diff --git a/bin/tests/StatsLogProcessor_test.cpp b/bin/tests/StatsLogProcessor_test.cpp
index 6e304b5..26b64f8 100644
--- a/bin/tests/StatsLogProcessor_test.cpp
+++ b/bin/tests/StatsLogProcessor_test.cpp
@@ -896,8 +896,8 @@
EXPECT_TRUE(metricProducer2->isActive());
int i = 0;
- for (; i < metricsManager1->mAllAtomMatchers.size(); i++) {
- if (metricsManager1->mAllAtomMatchers[i]->getId() ==
+ for (; i < metricsManager1->mAllAtomMatchingTrackers.size(); i++) {
+ if (metricsManager1->mAllAtomMatchingTrackers[i]->getId() ==
metric1ActivationTrigger1->atom_matcher_id()) {
break;
}
@@ -908,8 +908,8 @@
EXPECT_EQ(kNotActive, activation1->state);
i = 0;
- for (; i < metricsManager1->mAllAtomMatchers.size(); i++) {
- if (metricsManager1->mAllAtomMatchers[i]->getId() ==
+ for (; i < metricsManager1->mAllAtomMatchingTrackers.size(); i++) {
+ if (metricsManager1->mAllAtomMatchingTrackers[i]->getId() ==
metric1ActivationTrigger2->atom_matcher_id()) {
break;
}
@@ -981,8 +981,8 @@
EXPECT_TRUE(metricProducer1002->isActive());
i = 0;
- for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) {
- if (metricsManager1001->mAllAtomMatchers[i]->getId() ==
+ for (; i < metricsManager1001->mAllAtomMatchingTrackers.size(); i++) {
+ if (metricsManager1001->mAllAtomMatchingTrackers[i]->getId() ==
metric1ActivationTrigger1->atom_matcher_id()) {
break;
}
@@ -993,8 +993,8 @@
EXPECT_EQ(kNotActive, activation1001_1->state);
i = 0;
- for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) {
- if (metricsManager1001->mAllAtomMatchers[i]->getId() ==
+ for (; i < metricsManager1001->mAllAtomMatchingTrackers.size(); i++) {
+ if (metricsManager1001->mAllAtomMatchingTrackers[i]->getId() ==
metric1ActivationTrigger2->atom_matcher_id()) {
break;
}
@@ -1082,8 +1082,8 @@
EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
i = 0;
- for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) {
- if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() ==
+ for (; i < metricsManagerTimeBase3->mAllAtomMatchingTrackers.size(); i++) {
+ if (metricsManagerTimeBase3->mAllAtomMatchingTrackers[i]->getId() ==
metric1ActivationTrigger1->atom_matcher_id()) {
break;
}
@@ -1094,8 +1094,8 @@
EXPECT_EQ(kNotActive, activationTimeBase3_1->state);
i = 0;
- for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) {
- if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() ==
+ for (; i < metricsManagerTimeBase3->mAllAtomMatchingTrackers.size(); i++) {
+ if (metricsManagerTimeBase3->mAllAtomMatchingTrackers[i]->getId() ==
metric1ActivationTrigger2->atom_matcher_id()) {
break;
}
@@ -1184,8 +1184,8 @@
EXPECT_TRUE(metricProducerTimeBase4_2->isActive());
i = 0;
- for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) {
- if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() ==
+ for (; i < metricsManagerTimeBase4->mAllAtomMatchingTrackers.size(); i++) {
+ if (metricsManagerTimeBase4->mAllAtomMatchingTrackers[i]->getId() ==
metric1ActivationTrigger1->atom_matcher_id()) {
break;
}
@@ -1196,8 +1196,8 @@
EXPECT_EQ(kNotActive, activationTimeBase4_1->state);
i = 0;
- for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) {
- if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() ==
+ for (; i < metricsManagerTimeBase4->mAllAtomMatchingTrackers.size(); i++) {
+ if (metricsManagerTimeBase4->mAllAtomMatchingTrackers[i]->getId() ==
metric1ActivationTrigger2->atom_matcher_id()) {
break;
}
@@ -1585,8 +1585,8 @@
EXPECT_TRUE(metricProducer3->isActive());
// Check event activations.
- ASSERT_EQ(metricsManager1->mAllAtomMatchers.size(), 4);
- EXPECT_EQ(metricsManager1->mAllAtomMatchers[0]->getId(),
+ ASSERT_EQ(metricsManager1->mAllAtomMatchingTrackers.size(), 4);
+ EXPECT_EQ(metricsManager1->mAllAtomMatchingTrackers[0]->getId(),
metric1ActivationTrigger1->atom_matcher_id());
const auto& activation1 = metricProducer1->mEventActivationMap.at(0);
EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
@@ -1594,7 +1594,7 @@
EXPECT_EQ(kNotActive, activation1->state);
EXPECT_EQ(ACTIVATE_ON_BOOT, activation1->activationType);
- EXPECT_EQ(metricsManager1->mAllAtomMatchers[1]->getId(),
+ EXPECT_EQ(metricsManager1->mAllAtomMatchingTrackers[1]->getId(),
metric1ActivationTrigger2->atom_matcher_id());
const auto& activation2 = metricProducer1->mEventActivationMap.at(1);
EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns);
@@ -1602,7 +1602,7 @@
EXPECT_EQ(kNotActive, activation2->state);
EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2->activationType);
- EXPECT_EQ(metricsManager1->mAllAtomMatchers[2]->getId(),
+ EXPECT_EQ(metricsManager1->mAllAtomMatchingTrackers[2]->getId(),
metric2ActivationTrigger1->atom_matcher_id());
const auto& activation3 = metricProducer2->mEventActivationMap.at(2);
EXPECT_EQ(100 * NS_PER_SEC, activation3->ttl_ns);
@@ -1610,7 +1610,7 @@
EXPECT_EQ(kNotActive, activation3->state);
EXPECT_EQ(ACTIVATE_ON_BOOT, activation3->activationType);
- EXPECT_EQ(metricsManager1->mAllAtomMatchers[3]->getId(),
+ EXPECT_EQ(metricsManager1->mAllAtomMatchingTrackers[3]->getId(),
metric2ActivationTrigger2->atom_matcher_id());
const auto& activation4 = metricProducer2->mEventActivationMap.at(3);
EXPECT_EQ(200 * NS_PER_SEC, activation4->ttl_ns);
@@ -1685,8 +1685,8 @@
// Activation 1 is kActiveOnBoot.
// Activation 2 and 3 are not active.
// Activation 4 is active.
- ASSERT_EQ(metricsManager2->mAllAtomMatchers.size(), 4);
- EXPECT_EQ(metricsManager2->mAllAtomMatchers[0]->getId(),
+ ASSERT_EQ(metricsManager2->mAllAtomMatchingTrackers.size(), 4);
+ EXPECT_EQ(metricsManager2->mAllAtomMatchingTrackers[0]->getId(),
metric1ActivationTrigger1->atom_matcher_id());
const auto& activation1001 = metricProducer1001->mEventActivationMap.at(0);
EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns);
@@ -1694,7 +1694,7 @@
EXPECT_EQ(kActiveOnBoot, activation1001->state);
EXPECT_EQ(ACTIVATE_ON_BOOT, activation1001->activationType);
- EXPECT_EQ(metricsManager2->mAllAtomMatchers[1]->getId(),
+ EXPECT_EQ(metricsManager2->mAllAtomMatchingTrackers[1]->getId(),
metric1ActivationTrigger2->atom_matcher_id());
const auto& activation1002 = metricProducer1001->mEventActivationMap.at(1);
EXPECT_EQ(200 * NS_PER_SEC, activation1002->ttl_ns);
@@ -1702,7 +1702,7 @@
EXPECT_EQ(kNotActive, activation1002->state);
EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1002->activationType);
- EXPECT_EQ(metricsManager2->mAllAtomMatchers[2]->getId(),
+ EXPECT_EQ(metricsManager2->mAllAtomMatchingTrackers[2]->getId(),
metric2ActivationTrigger1->atom_matcher_id());
const auto& activation1003 = metricProducer1002->mEventActivationMap.at(2);
EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns);
@@ -1710,7 +1710,7 @@
EXPECT_EQ(kNotActive, activation1003->state);
EXPECT_EQ(ACTIVATE_ON_BOOT, activation1003->activationType);
- EXPECT_EQ(metricsManager2->mAllAtomMatchers[3]->getId(),
+ EXPECT_EQ(metricsManager2->mAllAtomMatchingTrackers[3]->getId(),
metric2ActivationTrigger2->atom_matcher_id());
const auto& activation1004 = metricProducer1002->mEventActivationMap.at(3);
EXPECT_EQ(200 * NS_PER_SEC, activation1004->ttl_ns);
diff --git a/bin/tests/condition/ConditionTimer_test.cpp b/bin/tests/condition/ConditionTimer_test.cpp
index ea02cd3..46dc9a9 100644
--- a/bin/tests/condition/ConditionTimer_test.cpp
+++ b/bin/tests/condition/ConditionTimer_test.cpp
@@ -35,11 +35,11 @@
EXPECT_EQ(0, timer.mTimerNs);
timer.onConditionChanged(true, ct_start_time + 5);
- EXPECT_EQ(ct_start_time + 5, timer.mLastConditionTrueTimestampNs);
+ EXPECT_EQ(ct_start_time + 5, timer.mLastConditionChangeTimestampNs);
EXPECT_EQ(true, timer.mCondition);
EXPECT_EQ(95, timer.newBucketStart(ct_start_time + 100));
- EXPECT_EQ(ct_start_time + 100, timer.mLastConditionTrueTimestampNs);
+ EXPECT_EQ(ct_start_time + 100, timer.mLastConditionChangeTimestampNs);
EXPECT_EQ(true, timer.mCondition);
}
@@ -51,7 +51,7 @@
EXPECT_EQ(ct_start_time - time_base, timer.newBucketStart(ct_start_time));
EXPECT_EQ(true, timer.mCondition);
EXPECT_EQ(0, timer.mTimerNs);
- EXPECT_EQ(ct_start_time, timer.mLastConditionTrueTimestampNs);
+ EXPECT_EQ(ct_start_time, timer.mLastConditionChangeTimestampNs);
timer.onConditionChanged(false, ct_start_time + 5);
EXPECT_EQ(5, timer.mTimerNs);
diff --git a/bin/tests/condition/SimpleConditionTracker_test.cpp b/bin/tests/condition/SimpleConditionTracker_test.cpp
index 07b5311..8998b5f 100644
--- a/bin/tests/condition/SimpleConditionTracker_test.cpp
+++ b/bin/tests/condition/SimpleConditionTracker_test.cpp
@@ -39,6 +39,7 @@
const int ATTRIBUTION_NODE_FIELD_ID = 1;
const int ATTRIBUTION_UID_FIELD_ID = 1;
const int TAG_ID = 1;
+const uint64_t protoHash = 0x123456789;
SimplePredicate getWakeLockHeldCondition(bool countNesting, bool defaultFalse,
bool outputSlicedUid, Position position) {
@@ -123,7 +124,7 @@
trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0;
trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1;
- SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"),
+ SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash,
0 /*tracker index*/, simplePredicate,
trackerNameIndexMap);
@@ -177,7 +178,7 @@
trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0;
trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1;
- SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"),
+ SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash,
0 /*tracker index*/, simplePredicate,
trackerNameIndexMap);
@@ -231,8 +232,9 @@
trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0;
trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1;
- SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), 0 /*tracker index*/,
- simplePredicate, trackerNameIndexMap);
+ SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash,
+ 0 /*tracker index*/, simplePredicate,
+ trackerNameIndexMap);
EXPECT_FALSE(conditionTracker.isSliced());
// This event is not accessed in this test besides dimensions which is why this is okay.
@@ -317,7 +319,7 @@
trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0;
trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1;
- SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"),
+ SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash,
0 /*condition tracker index*/, simplePredicate,
trackerNameIndexMap);
EXPECT_FALSE(conditionTracker.isSliced());
@@ -392,7 +394,7 @@
trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
- SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
+ SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), protoHash,
0 /*condition tracker index*/, simplePredicate,
trackerNameIndexMap);
@@ -514,7 +516,7 @@
trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
- SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
+ SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), protoHash,
0 /*condition tracker index*/, simplePredicate,
trackerNameIndexMap);
@@ -610,7 +612,7 @@
trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
- SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
+ SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), protoHash,
0 /*condition tracker index*/, simplePredicate,
trackerNameIndexMap);
diff --git a/bin/tests/e2e/ConfigUpdate_e2e_ab_test.cpp b/bin/tests/e2e/ConfigUpdate_e2e_ab_test.cpp
new file mode 100644
index 0000000..098f284
--- /dev/null
+++ b/bin/tests/e2e/ConfigUpdate_e2e_ab_test.cpp
@@ -0,0 +1,328 @@
+// Copyright (C) 2020 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 <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+
+#include "flags/flags.h"
+#include "src/StatsLogProcessor.h"
+#include "src/storage/StorageManager.h"
+#include "tests/statsd_test_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+#define STATS_DATA_DIR "/data/misc/stats-data"
+
+using android::base::SetProperty;
+using android::base::StringPrintf;
+using namespace std;
+
+namespace {
+
+StatsdConfig CreateSimpleConfig() {
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_STATSD");
+ config.set_hash_strings_in_metric_report(false);
+
+ *config.add_atom_matcher() = CreateBatteryStateUsbMatcher();
+ // Simple count metric so the config isn't empty.
+ CountMetric* countMetric1 = config.add_count_metric();
+ countMetric1->set_id(StringToId("Count1"));
+ countMetric1->set_what(config.atom_matcher(0).id());
+ countMetric1->set_bucket(FIVE_MINUTES);
+ return config;
+}
+} // namespace
+
+// Setup for parameterized tests.
+class ConfigUpdateE2eAbTest : public TestWithParam<bool> {
+private:
+ string originalFlagValue;
+
+public:
+ void SetUp() override {
+ originalFlagValue = getFlagBool(PARTIAL_CONFIG_UPDATE_FLAG, "");
+ string rawFlagName =
+ StringPrintf("persist.device_config.%s.%s", STATSD_NATIVE_NAMESPACE.c_str(),
+ PARTIAL_CONFIG_UPDATE_FLAG.c_str());
+ SetProperty(rawFlagName, GetParam() ? "true" : "false");
+ }
+
+ void TearDown() override {
+ string rawFlagName =
+ StringPrintf("persist.device_config.%s.%s", STATSD_NATIVE_NAMESPACE.c_str(),
+ PARTIAL_CONFIG_UPDATE_FLAG.c_str());
+ SetProperty(rawFlagName, originalFlagValue);
+ }
+};
+
+INSTANTIATE_TEST_SUITE_P(ConfigUpdateE2eAbTest, ConfigUpdateE2eAbTest, testing::Bool());
+
+TEST_P(ConfigUpdateE2eAbTest, TestUidMapVersionStringInstaller) {
+ sp<UidMap> uidMap = new UidMap();
+ vector<int32_t> uids({1000});
+ vector<int64_t> versions({1});
+ vector<String16> apps({String16("app1")});
+ vector<String16> versionStrings({String16("v1")});
+ vector<String16> installers({String16("installer1")});
+ uidMap->updateMap(1, uids, versions, versionStrings, apps, installers);
+
+ StatsdConfig config = CreateSimpleConfig();
+ config.set_version_strings_in_metric_report(true);
+ config.set_installer_in_metric_report(false);
+ int64_t baseTimeNs = getElapsedRealtimeNs();
+
+ ConfigKey cfgKey(0, 12345);
+ sp<StatsLogProcessor> processor =
+ CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey, nullptr, 0, uidMap);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+
+ // Now update.
+ config.set_version_strings_in_metric_report(false);
+ config.set_installer_in_metric_report(true);
+ processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_EQ(metricsManager == processor->mMetricsManagers.begin()->second, GetParam());
+ EXPECT_TRUE(metricsManager->isConfigValid());
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ // First report is written to disk when the update happens.
+ ASSERT_EQ(reports.reports_size(), 2);
+ UidMapping uidMapping = reports.reports(1).uid_map();
+ ASSERT_EQ(uidMapping.snapshots_size(), 1);
+ ASSERT_EQ(uidMapping.snapshots(0).package_info_size(), 1);
+ EXPECT_FALSE(uidMapping.snapshots(0).package_info(0).has_version_string());
+ EXPECT_EQ(uidMapping.snapshots(0).package_info(0).installer(), "installer1");
+}
+
+TEST_P(ConfigUpdateE2eAbTest, TestHashStrings) {
+ sp<UidMap> uidMap = new UidMap();
+ vector<int32_t> uids({1000});
+ vector<int64_t> versions({1});
+ vector<String16> apps({String16("app1")});
+ vector<String16> versionStrings({String16("v1")});
+ vector<String16> installers({String16("installer1")});
+ uidMap->updateMap(1, uids, versions, versionStrings, apps, installers);
+
+ StatsdConfig config = CreateSimpleConfig();
+ config.set_version_strings_in_metric_report(true);
+ config.set_hash_strings_in_metric_report(true);
+ int64_t baseTimeNs = getElapsedRealtimeNs();
+
+ ConfigKey cfgKey(0, 12345);
+ sp<StatsLogProcessor> processor =
+ CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey, nullptr, 0, uidMap);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+
+ // Now update.
+ config.set_hash_strings_in_metric_report(false);
+ processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_EQ(metricsManager == processor->mMetricsManagers.begin()->second, GetParam());
+ EXPECT_TRUE(metricsManager->isConfigValid());
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ // First report is written to disk when the update happens.
+ ASSERT_EQ(reports.reports_size(), 2);
+ UidMapping uidMapping = reports.reports(1).uid_map();
+ ASSERT_EQ(uidMapping.snapshots_size(), 1);
+ ASSERT_EQ(uidMapping.snapshots(0).package_info_size(), 1);
+ EXPECT_TRUE(uidMapping.snapshots(0).package_info(0).has_version_string());
+ EXPECT_FALSE(uidMapping.snapshots(0).package_info(0).has_version_string_hash());
+}
+
+TEST_P(ConfigUpdateE2eAbTest, TestAnnotations) {
+ StatsdConfig config = CreateSimpleConfig();
+ StatsdConfig_Annotation* annotation = config.add_annotation();
+ annotation->set_field_int64(11);
+ annotation->set_field_int32(1);
+ int64_t baseTimeNs = getElapsedRealtimeNs();
+ ConfigKey cfgKey(0, 12345);
+ sp<StatsLogProcessor> processor =
+ CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey);
+
+ // Now update
+ config.clear_annotation();
+ annotation = config.add_annotation();
+ annotation->set_field_int64(22);
+ annotation->set_field_int32(2);
+ processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config);
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ // First report is written to disk when the update happens.
+ ASSERT_EQ(reports.reports_size(), 2);
+ ConfigMetricsReport report = reports.reports(1);
+ EXPECT_EQ(report.annotation_size(), 1);
+ EXPECT_EQ(report.annotation(0).field_int64(), 22);
+ EXPECT_EQ(report.annotation(0).field_int32(), 2);
+}
+
+TEST_P(ConfigUpdateE2eAbTest, TestPersistLocally) {
+ StatsdConfig config = CreateSimpleConfig();
+ config.set_persist_locally(false);
+ int64_t baseTimeNs = getElapsedRealtimeNs();
+ ConfigKey cfgKey(0, 12345);
+ sp<StatsLogProcessor> processor =
+ CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey);
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ ASSERT_EQ(reports.reports_size(), 1);
+ // Number of reports should still be 1 since persist_locally is false.
+ reports.Clear();
+ buffer.clear();
+ processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ ASSERT_EQ(reports.reports_size(), 1);
+
+ // Now update.
+ config.set_persist_locally(true);
+ processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config);
+
+ // Should get 2: 1 in memory + 1 on disk. Both should be saved on disk.
+ reports.Clear();
+ buffer.clear();
+ processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ ASSERT_EQ(reports.reports_size(), 2);
+ // Should get 3, 2 on disk + 1 in memory.
+ reports.Clear();
+ buffer.clear();
+ processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ ASSERT_EQ(reports.reports_size(), 3);
+ string suffix = StringPrintf("%d_%lld", cfgKey.GetUid(), (long long)cfgKey.GetId());
+ StorageManager::deleteSuffixedFiles(STATS_DATA_DIR, suffix.c_str());
+ string historySuffix =
+ StringPrintf("%d_%lld_history", cfgKey.GetUid(), (long long)cfgKey.GetId());
+ StorageManager::deleteSuffixedFiles(STATS_DATA_DIR, historySuffix.c_str());
+}
+
+TEST_P(ConfigUpdateE2eAbTest, TestNoReportMetrics) {
+ StatsdConfig config = CreateSimpleConfig();
+ // Second simple count metric.
+ CountMetric* countMetric = config.add_count_metric();
+ countMetric->set_id(StringToId("Count2"));
+ countMetric->set_what(config.atom_matcher(0).id());
+ countMetric->set_bucket(FIVE_MINUTES);
+ config.add_no_report_metric(config.count_metric(0).id());
+ int64_t baseTimeNs = getElapsedRealtimeNs();
+ ConfigKey cfgKey(0, 12345);
+ sp<StatsLogProcessor> processor =
+ CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey);
+
+ // Now update.
+ config.clear_no_report_metric();
+ config.add_no_report_metric(config.count_metric(1).id());
+ processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config);
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ // First report is written to disk when the update happens.
+ ASSERT_EQ(reports.reports_size(), 2);
+ // First report (before update) has the first count metric.
+ ASSERT_EQ(reports.reports(0).metrics_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics(0).metric_id(), config.count_metric(1).id());
+ // Second report (after update) has the first count metric.
+ ASSERT_EQ(reports.reports(1).metrics_size(), 1);
+ EXPECT_EQ(reports.reports(1).metrics(0).metric_id(), config.count_metric(0).id());
+}
+
+TEST_P(ConfigUpdateE2eAbTest, TestAtomsAllowedFromAnyUid) {
+ StatsdConfig config = CreateSimpleConfig();
+ int64_t baseTimeNs = getElapsedRealtimeNs();
+ ConfigKey cfgKey(0, 12345);
+ sp<StatsLogProcessor> processor =
+ CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey);
+ // Uses AID_ROOT, which isn't in allowed log sources.
+ unique_ptr<LogEvent> event = CreateBatteryStateChangedEvent(
+ baseTimeNs + 2, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB);
+ processor->OnLogEvent(event.get());
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, baseTimeNs + 1001, true, true, ADB_DUMP, FAST, &buffer);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ ASSERT_EQ(reports.reports_size(), 1);
+ // Check the metric and make sure it has 0 count.
+ ASSERT_EQ(reports.reports(0).metrics_size(), 1);
+ EXPECT_FALSE(reports.reports(0).metrics(0).has_count_metrics());
+
+ // Now update. Allow plugged state to be logged from any uid, so the atom will be counted.
+ config.add_whitelisted_atom_ids(util::PLUGGED_STATE_CHANGED);
+ processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config);
+ unique_ptr<LogEvent> event2 = CreateBatteryStateChangedEvent(
+ baseTimeNs + 2000, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB);
+ processor->OnLogEvent(event.get());
+ reports.Clear();
+ buffer.clear();
+ processor->onDumpReport(cfgKey, baseTimeNs + 3000, true, true, ADB_DUMP, FAST, &buffer);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ ASSERT_EQ(reports.reports_size(), 2);
+ // Check the metric and make sure it has 0 count.
+ ASSERT_EQ(reports.reports(1).metrics_size(), 1);
+ EXPECT_TRUE(reports.reports(1).metrics(0).has_count_metrics());
+ ASSERT_EQ(reports.reports(1).metrics(0).count_metrics().data_size(), 1);
+ ASSERT_EQ(reports.reports(1).metrics(0).count_metrics().data(0).bucket_info_size(), 1);
+ EXPECT_EQ(reports.reports(1).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1);
+}
+
+TEST_P(ConfigUpdateE2eAbTest, TestConfigTtl) {
+ StatsdConfig config = CreateSimpleConfig();
+ config.set_ttl_in_seconds(1);
+ int64_t baseTimeNs = getElapsedRealtimeNs();
+ ConfigKey cfgKey(0, 12345);
+ sp<StatsLogProcessor> processor =
+ CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_EQ(metricsManager->getTtlEndNs(), baseTimeNs + NS_PER_SEC);
+
+ config.set_ttl_in_seconds(5);
+ processor->OnConfigUpdated(baseTimeNs + 2 * NS_PER_SEC, cfgKey, config);
+ metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_EQ(metricsManager->getTtlEndNs(), baseTimeNs + 7 * NS_PER_SEC);
+
+ // Clear the data stored on disk as a result of the update.
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, baseTimeNs + 3 * NS_PER_SEC, false, true, ADB_DUMP, FAST,
+ &buffer);
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/bin/tests/e2e/ConfigUpdate_e2e_test.cpp b/bin/tests/e2e/ConfigUpdate_e2e_test.cpp
new file mode 100644
index 0000000..94b778c
--- /dev/null
+++ b/bin/tests/e2e/ConfigUpdate_e2e_test.cpp
@@ -0,0 +1,358 @@
+// Copyright (C) 2020 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 <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+
+#include "flags/flags.h"
+#include "src/StatsLogProcessor.h"
+#include "src/storage/StorageManager.h"
+#include "tests/statsd_test_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+using android::base::SetProperty;
+using android::base::StringPrintf;
+using namespace std;
+
+// Tests that only run with the partial config update feature turned on.
+namespace {
+// Setup for test fixture.
+class ConfigUpdateE2eTest : public ::testing::Test {
+private:
+ string originalFlagValue;
+public:
+ void SetUp() override {
+ originalFlagValue = getFlagBool(PARTIAL_CONFIG_UPDATE_FLAG, "");
+ string rawFlagName =
+ StringPrintf("persist.device_config.%s.%s", STATSD_NATIVE_NAMESPACE.c_str(),
+ PARTIAL_CONFIG_UPDATE_FLAG.c_str());
+ SetProperty(rawFlagName, "true");
+ }
+
+ void TearDown() override {
+ string rawFlagName =
+ StringPrintf("persist.device_config.%s.%s", STATSD_NATIVE_NAMESPACE.c_str(),
+ PARTIAL_CONFIG_UPDATE_FLAG.c_str());
+ SetProperty(rawFlagName, originalFlagValue);
+ }
+};
+} // Anonymous namespace.
+
+TEST_F(ConfigUpdateE2eTest, TestNewDurationExistingWhat) {
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT");
+ *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
+ *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
+
+ Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+ *config.add_predicate() = holdingWakelockPredicate;
+
+ ConfigKey key(123, 987);
+ uint64_t bucketStartTimeNs = 10000000000; // 0:10
+ uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(FIVE_MINUTES) * 1000000LL;
+ sp<StatsLogProcessor> processor =
+ CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key);
+
+ int app1Uid = 123;
+ vector<int> attributionUids1 = {app1Uid};
+ vector<string> attributionTags1 = {"App1"};
+ // Create a wakelock acquire, causing the condition to be true.
+ unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC,
+ attributionUids1, attributionTags1,
+ "wl1"); // 0:10
+ processor->OnLogEvent(event.get());
+
+ // Add metric.
+ DurationMetric* durationMetric = config.add_duration_metric();
+ durationMetric->set_id(StringToId("WakelockDuration"));
+ durationMetric->set_what(holdingWakelockPredicate.id());
+ durationMetric->set_aggregation_type(DurationMetric::SUM);
+ durationMetric->set_bucket(FIVE_MINUTES);
+
+ uint64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; // 1:00
+ processor->OnConfigUpdated(updateTimeNs, key, config);
+
+ event = CreateReleaseWakelockEvent(bucketStartTimeNs + 80 * NS_PER_SEC, attributionUids1,
+ attributionTags1,
+ "wl1"); // 1:20
+ processor->OnLogEvent(event.get());
+ uint64_t dumpTimeNs = bucketStartTimeNs + 90 * NS_PER_SEC; // 1:30
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+ ASSERT_EQ(reports.reports_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics_size(), 1);
+ EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
+
+ StatsLogReport::DurationMetricDataWrapper metricData;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metricData);
+ ASSERT_EQ(metricData.data_size(), 1);
+ DurationMetricData data = metricData.data(0);
+ ASSERT_EQ(data.bucket_info_size(), 1);
+
+ DurationBucketInfo bucketInfo = data.bucket_info(0);
+ EXPECT_EQ(bucketInfo.start_bucket_elapsed_nanos(), updateTimeNs);
+ EXPECT_EQ(bucketInfo.end_bucket_elapsed_nanos(), dumpTimeNs);
+ EXPECT_EQ(bucketInfo.duration_nanos(), 20 * NS_PER_SEC);
+}
+
+TEST_F(ConfigUpdateE2eTest, TestNewDurationExistingWhatSlicedCondition) {
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT");
+ *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
+ *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
+ *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
+ *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
+
+ Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+ // The predicate is dimensioning by first attribution node by uid.
+ *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() =
+ CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ *config.add_predicate() = holdingWakelockPredicate;
+
+ Predicate isInBackgroundPredicate = CreateIsInBackgroundPredicate();
+ *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
+ CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /*uid*/});
+ *config.add_predicate() = isInBackgroundPredicate;
+
+ ConfigKey key(123, 987);
+ uint64_t bucketStartTimeNs = 10000000000; // 0:10
+ uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(FIVE_MINUTES) * 1000000LL;
+ sp<StatsLogProcessor> processor =
+ CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key);
+
+ int app1Uid = 123, app2Uid = 456;
+ vector<int> attributionUids1 = {app1Uid};
+ vector<string> attributionTags1 = {"App1"};
+ vector<int> attributionUids2 = {app2Uid};
+ vector<string> attributionTags2 = {"App2"};
+ unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC,
+ attributionUids1, attributionTags1,
+ "wl1"); // 0:10
+ processor->OnLogEvent(event.get());
+ event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, app1Uid); // 0:22
+ processor->OnLogEvent(event.get());
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + 35 * NS_PER_SEC, attributionUids2,
+ attributionTags2,
+ "wl1"); // 0:35
+ processor->OnLogEvent(event.get());
+
+ // Add metric.
+ DurationMetric* durationMetric = config.add_duration_metric();
+ durationMetric->set_id(StringToId("WakelockDuration"));
+ durationMetric->set_what(holdingWakelockPredicate.id());
+ durationMetric->set_condition(isInBackgroundPredicate.id());
+ durationMetric->set_aggregation_type(DurationMetric::SUM);
+ // The metric is dimensioning by first attribution node and only by uid.
+ *durationMetric->mutable_dimensions_in_what() =
+ CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ durationMetric->set_bucket(FIVE_MINUTES);
+ // Links between wakelock state atom and condition of app is in background.
+ auto links = durationMetric->add_links();
+ links->set_condition(isInBackgroundPredicate.id());
+ *links->mutable_fields_in_what() =
+ CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ *links->mutable_fields_in_condition() =
+ CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /*uid*/});
+
+ uint64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; // 1:00
+ processor->OnConfigUpdated(updateTimeNs, key, config);
+
+ event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 73 * NS_PER_SEC, app2Uid); // 1:13
+ processor->OnLogEvent(event.get());
+ event = CreateReleaseWakelockEvent(bucketStartTimeNs + 84 * NS_PER_SEC, attributionUids1,
+ attributionTags1, "wl1"); // 1:24
+ processor->OnLogEvent(event.get());
+
+ uint64_t dumpTimeNs = bucketStartTimeNs + 90 * NS_PER_SEC; // 1:30
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+ ASSERT_EQ(reports.reports_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics_size(), 1);
+ EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
+
+ StatsLogReport::DurationMetricDataWrapper metricData;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metricData);
+ ASSERT_EQ(metricData.data_size(), 2);
+
+ DurationMetricData data = metricData.data(0);
+ ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED,
+ app1Uid);
+ ASSERT_EQ(data.bucket_info_size(), 1);
+ DurationBucketInfo bucketInfo = data.bucket_info(0);
+ EXPECT_EQ(bucketInfo.duration_nanos(), 24 * NS_PER_SEC);
+
+ data = metricData.data(1);
+ ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED,
+ app2Uid);
+ ASSERT_EQ(data.bucket_info_size(), 1);
+ bucketInfo = data.bucket_info(0);
+ EXPECT_EQ(bucketInfo.duration_nanos(), 17 * NS_PER_SEC);
+}
+
+TEST_F(ConfigUpdateE2eTest, TestNewDurationExistingWhatSlicedState) {
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT");
+ *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
+ *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
+
+ Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+ // The predicate is dimensioning by first attribution node by uid.
+ *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() =
+ CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ *config.add_predicate() = holdingWakelockPredicate;
+
+ auto uidProcessState = CreateUidProcessState();
+ *config.add_state() = uidProcessState;
+
+ // Count metric. We don't care about this one. Only use it so the StateTracker gets persisted.
+ CountMetric* countMetric = config.add_count_metric();
+ countMetric->set_id(StringToId("Tmp"));
+ countMetric->set_what(config.atom_matcher(0).id());
+ countMetric->add_slice_by_state(uidProcessState.id());
+ // The metric is dimensioning by first attribution node and only by uid.
+ *countMetric->mutable_dimensions_in_what() =
+ CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ countMetric->set_bucket(FIVE_MINUTES);
+ auto stateLink = countMetric->add_state_link();
+ stateLink->set_state_atom_id(util::UID_PROCESS_STATE_CHANGED);
+ *stateLink->mutable_fields_in_what() =
+ CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ *stateLink->mutable_fields_in_state() =
+ CreateDimensions(util::UID_PROCESS_STATE_CHANGED, {1 /*uid*/});
+ config.add_no_report_metric(countMetric->id());
+
+ ConfigKey key(123, 987);
+ uint64_t bucketStartTimeNs = 10000000000; // 0:10
+ uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(FIVE_MINUTES) * 1000000LL;
+ sp<StatsLogProcessor> processor =
+ CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key);
+
+ int app1Uid = 123, app2Uid = 456;
+ vector<int> attributionUids1 = {app1Uid};
+ vector<string> attributionTags1 = {"App1"};
+ vector<int> attributionUids2 = {app2Uid};
+ vector<string> attributionTags2 = {"App2"};
+ unique_ptr<LogEvent> event = CreateUidProcessStateChangedEvent(
+ bucketStartTimeNs + 10 * NS_PER_SEC, app1Uid,
+ android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); // 0:10
+ processor->OnLogEvent(event.get());
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + 22 * NS_PER_SEC, attributionUids1,
+ attributionTags1,
+ "wl1"); // 0:22
+ processor->OnLogEvent(event.get());
+ event = CreateUidProcessStateChangedEvent(
+ bucketStartTimeNs + 30 * NS_PER_SEC, app2Uid,
+ android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); // 0:30
+ processor->OnLogEvent(event.get());
+
+ // Add metric.
+ DurationMetric* durationMetric = config.add_duration_metric();
+ durationMetric->set_id(StringToId("WakelockDuration"));
+ durationMetric->set_what(holdingWakelockPredicate.id());
+ durationMetric->add_slice_by_state(uidProcessState.id());
+ durationMetric->set_aggregation_type(DurationMetric::SUM);
+ // The metric is dimensioning by first attribution node and only by uid.
+ *durationMetric->mutable_dimensions_in_what() =
+ CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ durationMetric->set_bucket(FIVE_MINUTES);
+ // Links between wakelock state atom and condition of app is in background.
+ stateLink = durationMetric->add_state_link();
+ stateLink->set_state_atom_id(util::UID_PROCESS_STATE_CHANGED);
+ *stateLink->mutable_fields_in_what() =
+ CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ *stateLink->mutable_fields_in_state() =
+ CreateDimensions(util::UID_PROCESS_STATE_CHANGED, {1 /*uid*/});
+
+ uint64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; // 1:00
+ processor->OnConfigUpdated(updateTimeNs, key, config);
+
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + 72 * NS_PER_SEC, attributionUids2,
+ attributionTags2,
+ "wl1"); // 1:13
+ processor->OnLogEvent(event.get());
+ event = CreateUidProcessStateChangedEvent(
+ bucketStartTimeNs + 75 * NS_PER_SEC, app1Uid,
+ android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND); // 1:15
+ processor->OnLogEvent(event.get());
+ event = CreateReleaseWakelockEvent(bucketStartTimeNs + 84 * NS_PER_SEC, attributionUids1,
+ attributionTags1, "wl1"); // 1:24
+ processor->OnLogEvent(event.get());
+
+ uint64_t dumpTimeNs = bucketStartTimeNs + 90 * NS_PER_SEC; // 1:30
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+ ASSERT_EQ(reports.reports_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics_size(), 1);
+ EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
+
+ StatsLogReport::DurationMetricDataWrapper metricData;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metricData);
+ ASSERT_EQ(metricData.data_size(), 3);
+
+ DurationMetricData data = metricData.data(0);
+ ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED,
+ app1Uid);
+ ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED,
+ android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND);
+ ASSERT_EQ(data.bucket_info_size(), 1);
+ DurationBucketInfo bucketInfo = data.bucket_info(0);
+ EXPECT_EQ(bucketInfo.duration_nanos(), 15 * NS_PER_SEC);
+
+ data = metricData.data(1);
+ ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED,
+ app1Uid);
+ ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED,
+ android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND);
+ ASSERT_EQ(data.bucket_info_size(), 1);
+ bucketInfo = data.bucket_info(0);
+ EXPECT_EQ(bucketInfo.duration_nanos(), 9 * NS_PER_SEC);
+
+ data = metricData.data(2);
+ ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED,
+ app2Uid);
+ ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED,
+ android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND);
+ ASSERT_EQ(data.bucket_info_size(), 1);
+ bucketInfo = data.bucket_info(0);
+ EXPECT_EQ(bucketInfo.duration_nanos(), 18 * NS_PER_SEC);
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/bin/tests/e2e/DurationMetric_e2e_test.cpp b/bin/tests/e2e/DurationMetric_e2e_test.cpp
index 4efb038..2473c1c 100644
--- a/bin/tests/e2e/DurationMetric_e2e_test.cpp
+++ b/bin/tests/e2e/DurationMetric_e2e_test.cpp
@@ -486,11 +486,11 @@
// Links between wakelock state atom and condition of app is in background.
auto links = durationMetric->add_links();
links->set_condition(isInBackgroundPredicate.id());
- auto dimensionWhat = links->mutable_fields_in_what();
- dimensionWhat->set_field(util::WAKELOCK_STATE_CHANGED);
- dimensionWhat->add_child()->set_field(1); // uid field.
- *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
- util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST});
+ *links->mutable_fields_in_what() =
+ CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ auto dimensionCondition = links->mutable_fields_in_condition();
+ dimensionCondition->set_field(util::ACTIVITY_FOREGROUND_STATE_CHANGED);
+ dimensionCondition->add_child()->set_field(1); // uid field.
ConfigKey cfgKey;
uint64_t bucketStartTimeNs = 10000000000;
@@ -591,11 +591,11 @@
// Links between wakelock state atom and condition of app is in background.
auto links = durationMetric->add_links();
links->set_condition(isInBackgroundPredicate.id());
- auto dimensionWhat = links->mutable_fields_in_what();
- dimensionWhat->set_field(util::WAKELOCK_STATE_CHANGED);
- dimensionWhat->add_child()->set_field(1); // uid field.
- *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
- util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST});
+ *links->mutable_fields_in_what() =
+ CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ auto dimensionCondition = links->mutable_fields_in_condition();
+ dimensionCondition->set_field(util::ACTIVITY_FOREGROUND_STATE_CHANGED);
+ dimensionCondition->add_child()->set_field(1); // uid field.
auto metric_activation1 = config.add_metric_activation();
metric_activation1->set_metric_id(durationMetric->id());
@@ -1228,6 +1228,9 @@
*config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+ *(holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions()) =
+ CreateAttributionUidAndOtherDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST},
+ {3 /* tag */});
*config.add_predicate() = holdingWakelockPredicate;
auto uidProcessState = CreateUidProcessState();
diff --git a/bin/tests/external/StatsPullerManager_test.cpp b/bin/tests/external/StatsPullerManager_test.cpp
index c76e85e..0d539f4 100644
--- a/bin/tests/external/StatsPullerManager_test.cpp
+++ b/bin/tests/external/StatsPullerManager_test.cpp
@@ -89,10 +89,10 @@
sp<StatsPullerManager> createPullerManagerAndRegister() {
sp<StatsPullerManager> pullerManager = new StatsPullerManager();
shared_ptr<FakePullAtomCallback> cb1 = SharedRefBase::make<FakePullAtomCallback>(uid1);
- pullerManager->RegisterPullAtomCallback(uid1, pullTagId1, coolDownNs, timeoutNs, {}, cb1, true);
+ pullerManager->RegisterPullAtomCallback(uid1, pullTagId1, coolDownNs, timeoutNs, {}, cb1);
shared_ptr<FakePullAtomCallback> cb2 = SharedRefBase::make<FakePullAtomCallback>(uid2);
- pullerManager->RegisterPullAtomCallback(uid2, pullTagId1, coolDownNs, timeoutNs, {}, cb2, true);
- pullerManager->RegisterPullAtomCallback(uid1, pullTagId2, coolDownNs, timeoutNs, {}, cb1, true);
+ pullerManager->RegisterPullAtomCallback(uid2, pullTagId1, coolDownNs, timeoutNs, {}, cb2);
+ pullerManager->RegisterPullAtomCallback(uid1, pullTagId2, coolDownNs, timeoutNs, {}, cb1);
return pullerManager;
}
} // anonymous namespace
@@ -101,14 +101,14 @@
sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
vector<shared_ptr<LogEvent>> data;
- EXPECT_FALSE(pullerManager->Pull(pullTagId1, {unregisteredUid}, /*timestamp =*/1, &data, true));
+ EXPECT_FALSE(pullerManager->Pull(pullTagId1, {unregisteredUid}, /*timestamp =*/1, &data));
}
TEST(StatsPullerManagerTest, TestPullChoosesCorrectUid) {
sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
vector<shared_ptr<LogEvent>> data;
- EXPECT_TRUE(pullerManager->Pull(pullTagId1, {uid1}, /*timestamp =*/1, &data, true));
+ EXPECT_TRUE(pullerManager->Pull(pullTagId1, {uid1}, /*timestamp =*/1, &data));
ASSERT_EQ(data.size(), 1);
EXPECT_EQ(data[0]->GetTagId(), pullTagId1);
ASSERT_EQ(data[0]->getValues().size(), 1);
@@ -121,7 +121,7 @@
pullerManager->RegisterPullUidProvider(configKey, uidProvider);
vector<shared_ptr<LogEvent>> data;
- EXPECT_FALSE(pullerManager->Pull(pullTagId1, badConfigKey, /*timestamp =*/1, &data, true));
+ EXPECT_FALSE(pullerManager->Pull(pullTagId1, badConfigKey, /*timestamp =*/1, &data));
}
TEST(StatsPullerManagerTest, TestPullConfigKeyGood) {
@@ -130,7 +130,7 @@
pullerManager->RegisterPullUidProvider(configKey, uidProvider);
vector<shared_ptr<LogEvent>> data;
- EXPECT_TRUE(pullerManager->Pull(pullTagId1, configKey, /*timestamp =*/1, &data, true));
+ EXPECT_TRUE(pullerManager->Pull(pullTagId1, configKey, /*timestamp =*/1, &data));
EXPECT_EQ(data[0]->GetTagId(), pullTagId1);
ASSERT_EQ(data[0]->getValues().size(), 1);
EXPECT_EQ(data[0]->getValues()[0].mValue.int_value, uid2);
@@ -142,7 +142,7 @@
pullerManager->RegisterPullUidProvider(configKey, uidProvider);
vector<shared_ptr<LogEvent>> data;
- EXPECT_FALSE(pullerManager->Pull(pullTagId2, configKey, /*timestamp =*/1, &data, true));
+ EXPECT_FALSE(pullerManager->Pull(pullTagId2, configKey, /*timestamp =*/1, &data));
}
} // namespace statsd
diff --git a/bin/tests/guardrail/StatsdStats_test.cpp b/bin/tests/guardrail/StatsdStats_test.cpp
index 428c46f..16ae9b5 100644
--- a/bin/tests/guardrail/StatsdStats_test.cpp
+++ b/bin/tests/guardrail/StatsdStats_test.cpp
@@ -345,6 +345,9 @@
// old event, we get it from the stats buffer. should be ignored.
stats.noteBucketDropped(1000L);
+ stats.noteLateLogEvent(1000L, 10L);
+ stats.noteLateLogEvent(1000L, 50L);
+
stats.noteBucketBoundaryDelayNs(1000L, -1L);
stats.noteBucketBoundaryDelayNs(1000L, -10L);
stats.noteBucketBoundaryDelayNs(1000L, 2L);
@@ -364,12 +367,18 @@
EXPECT_EQ(1L, atomStats.bucket_dropped());
EXPECT_EQ(-10L, atomStats.min_bucket_boundary_delay_ns());
EXPECT_EQ(2L, atomStats.max_bucket_boundary_delay_ns());
+ EXPECT_EQ(2L, atomStats.late_log_event());
+ EXPECT_EQ(60L, atomStats.sum_late_log_event_extra_duration_ns());
+ EXPECT_EQ(50L, atomStats.max_late_log_event_extra_duration_ns());
auto atomStats2 = report.atom_metric_stats(1);
EXPECT_EQ(1001L, atomStats2.metric_id());
EXPECT_EQ(0L, atomStats2.bucket_dropped());
EXPECT_EQ(0L, atomStats2.min_bucket_boundary_delay_ns());
EXPECT_EQ(1L, atomStats2.max_bucket_boundary_delay_ns());
+ EXPECT_EQ(0L, atomStats2.late_log_event());
+ EXPECT_EQ(0L, atomStats2.sum_late_log_event_extra_duration_ns());
+ EXPECT_EQ(0L, atomStats2.max_late_log_event_extra_duration_ns());
}
TEST(StatsdStatsTest, TestAnomalyMonitor) {
diff --git a/bin/tests/metrics/CountMetricProducer_test.cpp b/bin/tests/metrics/CountMetricProducer_test.cpp
index bb8e7bf..8e2864c 100644
--- a/bin/tests/metrics/CountMetricProducer_test.cpp
+++ b/bin/tests/metrics/CountMetricProducer_test.cpp
@@ -41,6 +41,7 @@
namespace {
const ConfigKey kConfigKey(0, 12345);
+const uint64_t protoHash = 0x1234567890;
void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
@@ -75,7 +76,7 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2);
+ wizard, protoHash, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2);
EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(10, countProducer.mCurrentBucketNum);
EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs());
@@ -95,7 +96,7 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, bucketStartTimeNs, bucketStartTimeNs);
+ wizard, protoHash, bucketStartTimeNs, bucketStartTimeNs);
// 2 events in bucket 1.
LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -158,7 +159,7 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
CountMetricProducer countProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard,
- bucketStartTimeNs, bucketStartTimeNs);
+ protoHash, bucketStartTimeNs, bucketStartTimeNs);
countProducer.onConditionChanged(true, bucketStartTimeNs);
@@ -226,8 +227,8 @@
EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue));
CountMetricProducer countProducer(kConfigKey, metric, 0 /*condition tracker index*/,
- {ConditionState::kUnknown}, wizard, bucketStartTimeNs,
- bucketStartTimeNs);
+ {ConditionState::kUnknown}, wizard, protoHash,
+ bucketStartTimeNs, bucketStartTimeNs);
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
countProducer.flushIfNeededLocked(bucketStartTimeNs + 1);
@@ -265,7 +266,7 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard,
- bucketStartTimeNs, bucketStartTimeNs);
+ protoHash, bucketStartTimeNs, bucketStartTimeNs);
sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
EXPECT_TRUE(anomalyTracker != nullptr);
@@ -332,7 +333,7 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard,
- bucketStartTimeNs, bucketStartTimeNs);
+ protoHash, bucketStartTimeNs, bucketStartTimeNs);
// Bucket is flushed yet.
LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -397,7 +398,7 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, bucketStartTimeNs, bucketStartTimeNs);
+ wizard, protoHash, bucketStartTimeNs, bucketStartTimeNs);
sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
@@ -459,7 +460,7 @@
int64_t fiveWeeksNs = 5 * 7 * oneDayNs;
CountMetricProducer countProducer(kConfigKey, metric, -1 /* meaning no condition */, {}, wizard,
- oneDayNs, fiveWeeksNs);
+ protoHash, oneDayNs, fiveWeeksNs);
int64_t fiveWeeksOneDayNs = fiveWeeksNs + oneDayNs;
diff --git a/bin/tests/metrics/DurationMetricProducer_test.cpp b/bin/tests/metrics/DurationMetricProducer_test.cpp
index 05cfa37..bb2ede4 100644
--- a/bin/tests/metrics/DurationMetricProducer_test.cpp
+++ b/bin/tests/metrics/DurationMetricProducer_test.cpp
@@ -45,6 +45,7 @@
namespace {
const ConfigKey kConfigKey(0, 12345);
+const uint64_t protoHash = 0x1234567890;
void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, atomId);
@@ -71,10 +72,10 @@
FieldMatcher dimensions;
- DurationMetricProducer durationProducer(kConfigKey, metric, -1 /*no condition*/, {},
- 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard,
- dimensions, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2);
+ DurationMetricProducer durationProducer(
+ kConfigKey, metric, -1 /*no condition*/, {}, -1 /*what index not needed*/,
+ 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
+ wizard, protoHash, dimensions, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2);
EXPECT_EQ(600500000000, durationProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(10, durationProducer.mCurrentBucketNum);
@@ -99,10 +100,10 @@
FieldMatcher dimensions;
- DurationMetricProducer durationProducer(kConfigKey, metric, -1 /*no condition*/, {},
- 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard,
- dimensions, bucketStartTimeNs, bucketStartTimeNs);
+ DurationMetricProducer durationProducer(
+ kConfigKey, metric, -1 /*no condition*/, {}, -1 /*what index not needed*/,
+ 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
+ wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
@@ -144,8 +145,9 @@
DurationMetricProducer durationProducer(
kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown},
- 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
- wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
+ -1 /*what index not needed*/, 1 /* start index */, 2 /* stop index */,
+ 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
+ bucketStartTimeNs, bucketStartTimeNs);
durationProducer.mCondition = ConditionState::kFalse;
EXPECT_FALSE(durationProducer.mCondition);
@@ -194,8 +196,9 @@
DurationMetricProducer durationProducer(
kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown},
- 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
- wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
+ -1 /*what index not needed*/, 1 /* start index */, 2 /* stop index */,
+ 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
+ bucketStartTimeNs, bucketStartTimeNs);
EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition);
EXPECT_FALSE(durationProducer.isConditionSliced());
@@ -238,10 +241,10 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
FieldMatcher dimensions;
- DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {},
- 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard,
- dimensions, bucketStartTimeNs, bucketStartTimeNs);
+ DurationMetricProducer durationProducer(
+ kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
+ 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
+ wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -301,10 +304,10 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
FieldMatcher dimensions;
- DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {},
- 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard,
- dimensions, bucketStartTimeNs, bucketStartTimeNs);
+ DurationMetricProducer durationProducer(
+ kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
+ 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
+ wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -365,10 +368,10 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
FieldMatcher dimensions;
- DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {},
- 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard,
- dimensions, bucketStartTimeNs, bucketStartTimeNs);
+ DurationMetricProducer durationProducer(
+ kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
+ 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
+ wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor);
EXPECT_TRUE(anomalyTracker != nullptr);
@@ -411,10 +414,10 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
FieldMatcher dimensions;
- DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {},
- 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard,
- dimensions, bucketStartTimeNs, bucketStartTimeNs);
+ DurationMetricProducer durationProducer(
+ kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
+ 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
+ wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
int64_t startTimeNs = bucketStartTimeNs + 1;
LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -465,10 +468,10 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
FieldMatcher dimensions;
- DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {},
- 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard,
- dimensions, bucketStartTimeNs, bucketStartTimeNs);
+ DurationMetricProducer durationProducer(
+ kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
+ 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
+ wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
int64_t startTimeNs = bucketStartTimeNs + 1;
LogEvent event1(/*uid=*/0, /*pid=*/0);
diff --git a/bin/tests/metrics/EventMetricProducer_test.cpp b/bin/tests/metrics/EventMetricProducer_test.cpp
index dfbb9da..4bbbd2c 100644
--- a/bin/tests/metrics/EventMetricProducer_test.cpp
+++ b/bin/tests/metrics/EventMetricProducer_test.cpp
@@ -36,9 +36,11 @@
namespace os {
namespace statsd {
-const ConfigKey kConfigKey(0, 12345);
namespace {
+const ConfigKey kConfigKey(0, 12345);
+const uint64_t protoHash = 0x1234567890;
+
void makeLogEvent(LogEvent* logEvent, int32_t atomId, int64_t timestampNs, string str) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, atomId);
@@ -66,7 +68,7 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, bucketStartTimeNs);
+ wizard, protoHash, bucketStartTimeNs);
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
@@ -102,7 +104,8 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
EventMetricProducer eventProducer(kConfigKey, metric, 0 /*condition index*/,
- {ConditionState::kUnknown}, wizard, bucketStartTimeNs);
+ {ConditionState::kUnknown}, wizard, protoHash,
+ bucketStartTimeNs);
eventProducer.onConditionChanged(true /*condition*/, bucketStartTimeNs);
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
@@ -157,7 +160,8 @@
EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue));
EventMetricProducer eventProducer(kConfigKey, metric, 0 /*condition index*/,
- {ConditionState::kUnknown}, wizard, bucketStartTimeNs);
+ {ConditionState::kUnknown}, wizard, protoHash,
+ bucketStartTimeNs);
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
diff --git a/bin/tests/metrics/GaugeMetricProducer_test.cpp b/bin/tests/metrics/GaugeMetricProducer_test.cpp
index 5997bed..1060681 100644
--- a/bin/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/bin/tests/metrics/GaugeMetricProducer_test.cpp
@@ -23,7 +23,7 @@
#include "logd/LogEvent.h"
#include "metrics_test_helper.h"
-#include "src/matchers/SimpleLogMatchingTracker.h"
+#include "src/matchers/SimpleAtomMatchingTracker.h"
#include "src/metrics/MetricProducer.h"
#include "src/stats_log_util.h"
#include "stats_event.h"
@@ -47,7 +47,7 @@
const ConfigKey kConfigKey(0, 12345);
const int tagId = 1;
const int64_t metricId = 123;
-const int64_t atomMatcherId = 678;
+const uint64_t protoHash = 0x123456789;
const int logEventMatcherIndex = 0;
const int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
@@ -94,19 +94,17 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
- sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
- new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<EventMatcherWizard> eventMatcherWizard =
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
// statsd started long ago.
// The metric starts in the middle of the bucket
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, logEventMatcherIndex, eventMatcherWizard, -1, -1,
- tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager);
+ wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+ -1, -1, tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2,
+ pullerManager);
gaugeProducer.prepareFirstBucket();
EXPECT_EQ(600500000000, gaugeProducer.mCurrentBucketStartTimeNs);
@@ -127,19 +125,15 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(makeLogEvent(tagId, eventTimeNs + 10, 3, "some value", 11));
@@ -147,8 +141,9 @@
}));
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1,
- tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+ wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+ tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
+ pullerManager);
gaugeProducer.prepareFirstBucket();
vector<shared_ptr<LogEvent>> allData;
@@ -217,15 +212,11 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, logEventMatcherIndex, eventMatcherWizard,
+ wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
-1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
gaugeProducer.prepareFirstBucket();
@@ -301,19 +292,16 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
+
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Return(false))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 2));
@@ -321,8 +309,9 @@
}));
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1,
- tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+ wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+ tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
+ pullerManager);
gaugeProducer.prepareFirstBucket();
vector<shared_ptr<LogEvent>> allData;
@@ -378,22 +367,19 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Return(false));
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1,
- tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+ wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+ tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
+ pullerManager);
gaugeProducer.prepareFirstBucket();
vector<shared_ptr<LogEvent>> allData;
@@ -428,30 +414,26 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
int64_t conditionChangeNs = bucketStartTimeNs + 8;
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, conditionChangeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, conditionChangeNs, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs + 10, 100));
return true;
}));
GaugeMetricProducer gaugeProducer(kConfigKey, metric, 0 /*condition index*/,
- {ConditionState::kUnknown}, wizard, logEventMatcherIndex,
- eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs,
- bucketStartTimeNs, pullerManager);
+ {ConditionState::kUnknown}, wizard, protoHash,
+ logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
gaugeProducer.prepareFirstBucket();
gaugeProducer.onConditionChanged(true, conditionChangeNs);
@@ -502,12 +484,8 @@
dim->set_field(tagId);
dim->add_child()->set_field(1);
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
EXPECT_CALL(*wizard, query(_, _, _))
@@ -527,18 +505,18 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, sliceConditionChangeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, sliceConditionChangeNs, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs + 10, 1000, 100));
return true;
}));
GaugeMetricProducer gaugeProducer(kConfigKey, metric, 0 /*condition index*/,
- {ConditionState::kUnknown}, wizard, logEventMatcherIndex,
- eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs,
- bucketStartTimeNs, pullerManager);
+ {ConditionState::kUnknown}, wizard, protoHash,
+ logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
gaugeProducer.prepareFirstBucket();
gaugeProducer.onSlicedConditionMayChange(true, sliceConditionChangeNs);
@@ -566,7 +544,7 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Return(false));
GaugeMetric metric;
@@ -577,16 +555,13 @@
gaugeFieldMatcher->set_field(tagId);
gaugeFieldMatcher->add_child()->set_field(2);
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1,
- tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+ wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+ tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
+ pullerManager);
gaugeProducer.prepareFirstBucket();
Alert alert;
@@ -657,24 +632,20 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 4));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 5));
@@ -684,8 +655,8 @@
int triggerId = 5;
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, logEventMatcherIndex, eventMatcherWizard, tagId,
- triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
+ wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+ tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
gaugeProducer.prepareFirstBucket();
@@ -729,31 +700,27 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 3, 4));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 4, 5));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 4, 6));
@@ -763,8 +730,8 @@
int triggerId = 5;
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, logEventMatcherIndex, eventMatcherWizard, tagId,
- triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
+ wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+ tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
gaugeProducer.prepareFirstBucket();
@@ -807,18 +774,14 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 3, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 3, _))
// Bucket start.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 10));
return true;
@@ -826,8 +789,8 @@
int triggerId = 5;
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, logEventMatcherIndex, eventMatcherWizard, tagId,
- triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
+ wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+ tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
gaugeProducer.prepareFirstBucket();
diff --git a/bin/tests/metrics/ValueMetricProducer_test.cpp b/bin/tests/metrics/ValueMetricProducer_test.cpp
index 5666501..6cf4192 100644
--- a/bin/tests/metrics/ValueMetricProducer_test.cpp
+++ b/bin/tests/metrics/ValueMetricProducer_test.cpp
@@ -22,7 +22,7 @@
#include <vector>
#include "metrics_test_helper.h"
-#include "src/matchers/SimpleLogMatchingTracker.h"
+#include "src/matchers/SimpleAtomMatchingTracker.h"
#include "src/metrics/MetricProducer.h"
#include "src/stats_log_util.h"
#include "tests/statsd_test_util.h"
@@ -46,7 +46,7 @@
const ConfigKey kConfigKey(0, 12345);
const int tagId = 1;
const int64_t metricId = 123;
-const int64_t atomMatcherId = 678;
+const uint64_t protoHash = 0x1234567890;
const int logEventMatcherIndex = 0;
const int64_t bucketStartTimeNs = 10000000000;
const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
@@ -58,7 +58,7 @@
double epsilon = 0.001;
static void assertPastBucketValuesSingleKey(
- const std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>>& mPastBuckets,
+ const std::unordered_map<MetricDimensionKey, std::vector<PastValueBucket>>& mPastBuckets,
const std::initializer_list<int>& expectedValuesList,
const std::initializer_list<int64_t>& expectedDurationNsList,
const std::initializer_list<int64_t>& expectedStartTimeNsList,
@@ -80,7 +80,7 @@
ASSERT_EQ(1, mPastBuckets.size());
ASSERT_EQ(expectedValues.size(), mPastBuckets.begin()->second.size());
- const vector<ValueBucket>& buckets = mPastBuckets.begin()->second;
+ const vector<PastValueBucket>& buckets = mPastBuckets.begin()->second;
for (int i = 0; i < expectedValues.size(); i++) {
EXPECT_EQ(expectedValues[i], buckets[i].values[0].long_value)
<< "Values differ at index " << i;
@@ -93,18 +93,21 @@
}
}
+static void assertConditionTimer(const ConditionTimer& conditionTimer, bool condition,
+ int64_t timerNs, int64_t lastConditionTrueTimestampNs) {
+ EXPECT_EQ(condition, conditionTimer.mCondition);
+ EXPECT_EQ(timerNs, conditionTimer.mTimerNs);
+ EXPECT_EQ(lastConditionTrueTimestampNs, conditionTimer.mLastConditionChangeTimestampNs);
+}
+
} // anonymous namespace
class ValueMetricProducerTestHelper {
public:
static sp<ValueMetricProducer> createValueProducerNoConditions(
sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) {
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _))
.WillOnce(Return());
@@ -113,8 +116,8 @@
sp<ValueMetricProducer> valueProducer =
new ValueMetricProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, logEventMatcherIndex, eventMatcherWizard, tagId,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+ wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+ tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
valueProducer->prepareFirstBucket();
return valueProducer;
}
@@ -122,12 +125,8 @@
static sp<ValueMetricProducer> createValueProducerWithCondition(
sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric,
ConditionState conditionAfterFirstBucketPrepared) {
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _))
.WillOnce(Return());
@@ -136,7 +135,7 @@
sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
kConfigKey, metric, 0 /*condition index*/, {ConditionState::kUnknown}, wizard,
- logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs,
+ protoHash, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
valueProducer->prepareFirstBucket();
valueProducer->mCondition = conditionAfterFirstBucketPrepared;
@@ -147,12 +146,8 @@
sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric,
vector<int32_t> slicedStateAtoms,
unordered_map<int, unordered_map<int, int64_t>> stateGroupMap) {
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _))
.WillOnce(Return());
@@ -160,9 +155,9 @@
.WillRepeatedly(Return());
sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
- kConfigKey, metric, -1 /* no condition */, {}, wizard, logEventMatcherIndex,
- eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager, {},
- {}, slicedStateAtoms, stateGroupMap);
+ kConfigKey, metric, -1 /* no condition */, {}, wizard, protoHash,
+ logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs,
+ bucketStartTimeNs, pullerManager, {}, {}, slicedStateAtoms, stateGroupMap);
valueProducer->prepareFirstBucket();
return valueProducer;
}
@@ -172,12 +167,8 @@
vector<int32_t> slicedStateAtoms,
unordered_map<int, unordered_map<int, int64_t>> stateGroupMap,
ConditionState conditionAfterFirstBucketPrepared) {
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _))
.WillOnce(Return());
@@ -186,8 +177,9 @@
sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
kConfigKey, metric, 0 /* condition tracker index */, {ConditionState::kUnknown},
- wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs,
- bucketStartTimeNs, pullerManager, {}, {}, slicedStateAtoms, stateGroupMap);
+ wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager, {}, {}, slicedStateAtoms,
+ stateGroupMap);
valueProducer->prepareFirstBucket();
valueProducer->mCondition = conditionAfterFirstBucketPrepared;
return valueProducer;
@@ -237,20 +229,16 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
int64_t startTimeBase = 11;
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
// statsd started long ago.
// The metric starts in the middle of the bucket
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, logEventMatcherIndex, eventMatcherWizard, -1,
- startTimeBase, 22, pullerManager);
+ wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+ -1, startTimeBase, 22, pullerManager);
valueProducer.prepareFirstBucket();
EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
@@ -267,20 +255,16 @@
TEST(ValueMetricProducerTest, TestFirstBucket) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
// statsd started long ago.
// The metric starts in the middle of the bucket
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, logEventMatcherIndex, eventMatcherWizard, -1, 5,
- 600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager);
+ wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+ -1, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager);
valueProducer.prepareFirstBucket();
EXPECT_EQ(600500000000, valueProducer.mCurrentBucketStartTimeNs);
@@ -294,9 +278,9 @@
TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
return true;
@@ -313,8 +297,9 @@
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second[0];
- ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ValueMetricProducer::BaseInfo curBaseInfo =
+ valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(11, curBaseInfo.base.long_value);
@@ -329,8 +314,8 @@
valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(23, curBaseInfo.base.long_value);
@@ -347,8 +332,8 @@
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(36, curBaseInfo.base.long_value);
@@ -368,19 +353,19 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2;
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Initialize bucket.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1));
return true;
}))
// Partial bucket.
- .WillOnce(Invoke([partialBucketSplitTimeNs](
- int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
+ const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
data->clear();
data->push_back(
@@ -421,30 +406,27 @@
TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
- auto keyValue = atomMatcher.add_field_value_matcher();
- keyValue->set_field(1);
- keyValue->set_eq_int(3);
+ FieldValueMatcher fvm;
+ fvm.set_field(1);
+ fvm.set_eq_int(3);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex, {fvm});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 3, 3));
return true;
}));
- sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
- kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, wizard, logEventMatcherIndex,
- eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+ sp<ValueMetricProducer> valueProducer =
+ new ValueMetricProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, wizard,
+ protoHash, logEventMatcherIndex, eventMatcherWizard, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
valueProducer->prepareFirstBucket();
vector<shared_ptr<LogEvent>> allData;
@@ -455,8 +437,9 @@
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second[0];
- ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ValueMetricProducer::BaseInfo curBaseInfo =
+ valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(11, curBaseInfo.base.long_value);
@@ -484,8 +467,8 @@
allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 3, 36));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
// the base was reset
EXPECT_EQ(true, curBaseInfo.hasBase);
@@ -505,7 +488,7 @@
metric.set_use_absolute_value_on_reset(true);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Return(true));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
@@ -518,8 +501,9 @@
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second[0];
- ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ValueMetricProducer::BaseInfo curBaseInfo =
+ valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(11, curBaseInfo.base.long_value);
@@ -531,8 +515,8 @@
valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(10, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
@@ -545,8 +529,8 @@
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(36, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
@@ -565,7 +549,7 @@
TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Return(false));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
@@ -578,8 +562,9 @@
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second[0];
- ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ValueMetricProducer::BaseInfo curBaseInfo =
+ valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(11, curBaseInfo.base.long_value);
@@ -591,8 +576,8 @@
valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(10, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
@@ -602,8 +587,8 @@
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(36, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
@@ -621,23 +606,23 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // First condition change.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1); // Second condition change.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 130));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket3StartTimeNs + 1); // Third condition change.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 180));
@@ -653,8 +638,9 @@
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second[0];
- ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ValueMetricProducer::BaseInfo curBaseInfo =
+ valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
// startUpdated:false sum:0 start:100
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(100, curBaseInfo.base.long_value);
@@ -670,8 +656,8 @@
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(110, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
@@ -683,8 +669,8 @@
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(20, curInterval.value.long_value);
EXPECT_EQ(false, curBaseInfo.hasBase);
@@ -698,18 +684,14 @@
TEST_P(ValueMetricProducerTest_PartialBucket, TestPushedEvents) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
- eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
- pullerManager);
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+ logEventMatcherIndex, eventMatcherWizard, -1,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
valueProducer.prepareFirstBucket();
LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -759,31 +741,27 @@
TEST_P(ValueMetricProducerTest_PartialBucket, TestPulledValue) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 150;
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Return(true))
- .WillOnce(Invoke([partialBucketSplitTimeNs](
- int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
+ const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 120));
return true;
}));
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
- eventMatcherWizard, tagId, bucketStartTimeNs,
- bucketStartTimeNs, pullerManager);
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+ logEventMatcherIndex, eventMatcherWizard, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
valueProducer.prepareFirstBucket();
vector<shared_ptr<LogEvent>> allData;
@@ -820,22 +798,18 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
metric.set_split_bucket_for_app_upgrade(false);
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Return(true));
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
- eventMatcherWizard, tagId, bucketStartTimeNs,
- bucketStartTimeNs, pullerManager);
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+ logEventMatcherIndex, eventMatcherWizard, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
valueProducer.prepareFirstBucket();
vector<shared_ptr<LogEvent>> allData;
@@ -854,16 +828,16 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1); // Condition change to true time.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 100));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs,
bucket2StartTimeNs - 100); // Condition change to false time.
data->clear();
@@ -900,18 +874,14 @@
TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
- eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
- pullerManager);
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+ logEventMatcherIndex, eventMatcherWizard, -1,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
valueProducer.prepareFirstBucket();
LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -924,8 +894,9 @@
// has one slice
ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
- valueProducer.mCurrentSlicedBucket.begin()->second[0];
- ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+ valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+ ValueMetricProducer::BaseInfo curBaseInfo =
+ valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(10, curInterval.value.long_value);
EXPECT_EQ(true, curInterval.hasValue);
@@ -933,7 +904,7 @@
// has one slice
ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
EXPECT_EQ(30, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -944,17 +915,13 @@
TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
ValueMetricProducer valueProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard,
- logEventMatcherIndex, eventMatcherWizard, -1,
+ protoHash, logEventMatcherIndex, eventMatcherWizard, -1,
bucketStartTimeNs, bucketStartTimeNs, pullerManager);
valueProducer.prepareFirstBucket();
valueProducer.mCondition = ConditionState::kFalse;
@@ -974,8 +941,8 @@
// has one slice
ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
- valueProducer.mCurrentSlicedBucket.begin()->second[0];
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
EXPECT_EQ(20, curInterval.value.long_value);
LogEvent event3(/*uid=*/0, /*pid=*/0);
@@ -984,7 +951,7 @@
// has one slice
ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
EXPECT_EQ(50, curInterval.value.long_value);
valueProducer.onConditionChangedLocked(false, bucketStartTimeNs + 35);
@@ -995,7 +962,7 @@
// has one slice
ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
EXPECT_EQ(50, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -1015,17 +982,13 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, logEventMatcherIndex, eventMatcherWizard,
+ wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
-1 /*not pulled*/, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
valueProducer.prepareFirstBucket();
@@ -1081,11 +1044,54 @@
std::ceil(1.0 * event6.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
}
+TEST(ValueMetricProducerTest, TestAnomalyDetectionMultipleBucketsSkipped) {
+ sp<AlarmMonitor> alarmMonitor;
+ Alert alert;
+ alert.set_id(101);
+ alert.set_metric_id(metricId);
+ alert.set_trigger_if_sum_gt(100);
+ alert.set_num_buckets(1);
+ const int32_t refPeriodSec = 3;
+ alert.set_refractory_period_secs(refPeriodSec);
+
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1); // Condition change to true time.
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 0));
+ return true;
+ }))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs,
+ bucket3StartTimeNs + 100); // Condition changed to false time.
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 100, 120));
+ return true;
+ }));
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
+ ConditionState::kFalse);
+ sp<AnomalyTracker> anomalyTracker = valueProducer->addAnomalyTracker(alert, alarmMonitor);
+
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 1);
+
+ // multiple buckets should be skipped here.
+ valueProducer->onConditionChanged(false, bucket3StartTimeNs + 100);
+
+ // No alert is fired when multiple buckets are skipped.
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
+}
+
// Test value metric no condition, the pull on bucket boundary come in time and too late
TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Return(true));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
@@ -1099,8 +1105,9 @@
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second[0];
- ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ValueMetricProducer::BaseInfo curBaseInfo =
+ valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
// startUpdated:true sum:0 start:11
EXPECT_EQ(true, curBaseInfo.hasBase);
@@ -1114,8 +1121,8 @@
valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
// tartUpdated:false sum:12
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(23, curBaseInfo.base.long_value);
@@ -1131,8 +1138,8 @@
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket6StartTimeNs + 1, 36));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs);
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
// startUpdated:false sum:12
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(36, curBaseInfo.base.long_value);
@@ -1164,10 +1171,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// condition becomes true
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // First condition change.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
@@ -1175,7 +1182,7 @@
}))
// condition becomes false
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1); // Second condition change.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120));
@@ -1190,8 +1197,9 @@
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second[0];
- ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ValueMetricProducer::BaseInfo curBaseInfo =
+ valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(100, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
@@ -1199,8 +1207,8 @@
// pull on bucket boundary come late, condition change happens before it
valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
{bucketStartTimeNs}, {bucket2StartTimeNs});
EXPECT_EQ(false, curBaseInfo.hasBase);
@@ -1213,8 +1221,8 @@
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
{bucketStartTimeNs}, {bucket2StartTimeNs});
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(false, curBaseInfo.hasBase);
EXPECT_EQ(false, curInterval.hasValue);
}
@@ -1227,10 +1235,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// condition becomes true
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
@@ -1238,7 +1246,7 @@
}))
// condition becomes false
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120));
@@ -1246,7 +1254,7 @@
}))
// condition becomes true again
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 25);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 25, 130));
@@ -1262,8 +1270,9 @@
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second[0];
- ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ValueMetricProducer::BaseInfo curBaseInfo =
+ valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
// startUpdated:false sum:0 start:100
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(100, curBaseInfo.base.long_value);
@@ -1275,8 +1284,8 @@
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
{bucketStartTimeNs}, {bucket2StartTimeNs});
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(false, curBaseInfo.hasBase);
EXPECT_EQ(false, curInterval.hasValue);
@@ -1284,8 +1293,8 @@
valueProducer->onConditionChanged(true, bucket2StartTimeNs + 25);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
{bucketStartTimeNs}, {bucket2StartTimeNs});
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(130, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
@@ -1296,8 +1305,8 @@
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 50, 140));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 50);
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(140, curBaseInfo.base.long_value);
EXPECT_EQ(true, curInterval.hasValue);
@@ -1317,18 +1326,14 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
metric.set_aggregation_type(ValueMetric::MIN);
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
- eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
- pullerManager);
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+ logEventMatcherIndex, eventMatcherWizard, -1,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
valueProducer.prepareFirstBucket();
LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -1341,7 +1346,7 @@
// has one slice
ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
- valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
EXPECT_EQ(10, curInterval.value.long_value);
EXPECT_EQ(true, curInterval.hasValue);
@@ -1349,7 +1354,7 @@
// has one slice
ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
EXPECT_EQ(10, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -1361,18 +1366,14 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
metric.set_aggregation_type(ValueMetric::MAX);
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
- eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
- pullerManager);
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+ logEventMatcherIndex, eventMatcherWizard, -1,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
valueProducer.prepareFirstBucket();
LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -1382,7 +1383,7 @@
// has one slice
ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
- valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
EXPECT_EQ(10, curInterval.value.long_value);
EXPECT_EQ(true, curInterval.hasValue);
@@ -1392,7 +1393,7 @@
// has one slice
ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
EXPECT_EQ(20, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -1404,18 +1405,14 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
metric.set_aggregation_type(ValueMetric::AVG);
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
- eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
- pullerManager);
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+ logEventMatcherIndex, eventMatcherWizard, -1,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
valueProducer.prepareFirstBucket();
LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -1427,7 +1424,7 @@
// has one slice
ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval;
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
EXPECT_EQ(10, curInterval.value.long_value);
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(1, curInterval.sampleSize);
@@ -1436,7 +1433,7 @@
// has one slice
ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
EXPECT_EQ(25, curInterval.value.long_value);
EXPECT_EQ(2, curInterval.sampleSize);
@@ -1452,18 +1449,14 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
metric.set_aggregation_type(ValueMetric::SUM);
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
- eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
- pullerManager);
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+ logEventMatcherIndex, eventMatcherWizard, -1,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
valueProducer.prepareFirstBucket();
LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -1475,7 +1468,7 @@
// has one slice
ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
- valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
EXPECT_EQ(10, curInterval.value.long_value);
EXPECT_EQ(true, curInterval.hasValue);
@@ -1483,7 +1476,7 @@
// has one slice
ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
EXPECT_EQ(25, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -1496,18 +1489,14 @@
metric.set_aggregation_type(ValueMetric::MIN);
metric.set_use_diff(true);
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
- eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
- pullerManager);
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+ logEventMatcherIndex, eventMatcherWizard, -1,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
valueProducer.prepareFirstBucket();
LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -1517,8 +1506,9 @@
// has one slice
ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
- valueProducer.mCurrentSlicedBucket.begin()->second[0];
- ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+ valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+ ValueMetricProducer::BaseInfo curBaseInfo =
+ valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(10, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
@@ -1529,7 +1519,7 @@
// has one slice
ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(5, curInterval.value.long_value);
@@ -1539,8 +1529,8 @@
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
- curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(15, curBaseInfo.base.long_value);
EXPECT_EQ(true, curInterval.hasValue);
@@ -1550,8 +1540,8 @@
CreateRepeatedValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 15);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
- curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(15, curBaseInfo.base.long_value);
EXPECT_EQ(true, curInterval.hasValue);
@@ -1568,18 +1558,14 @@
metric.set_aggregation_type(ValueMetric::MIN);
metric.set_use_diff(true);
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
- eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
- pullerManager);
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+ logEventMatcherIndex, eventMatcherWizard, -1,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
valueProducer.prepareFirstBucket();
LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -1592,12 +1578,13 @@
// has one slice
ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
- valueProducer.mCurrentSlicedBucket.begin()->second[0];
- ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+ valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+ ValueMetricProducer::BaseInfo curBaseInfo =
+ valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(10, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
- curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(20, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
@@ -1606,12 +1593,12 @@
// has one slice
ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
- curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(5, curInterval.value.long_value);
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
- curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[1];
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1];
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(2, curInterval.value.long_value);
@@ -1621,14 +1608,14 @@
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
- curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(15, curBaseInfo.base.long_value);
EXPECT_EQ(true, curInterval.hasValue);
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
- curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[1];
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(25, curBaseInfo.base.long_value);
EXPECT_EQ(true, curInterval.hasValue);
@@ -1638,13 +1625,13 @@
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
- curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(15, curBaseInfo.base.long_value);
EXPECT_EQ(true, curInterval.hasValue);
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
- curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[1];
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(29, curBaseInfo.base.long_value);
EXPECT_EQ(true, curInterval.hasValue);
@@ -1677,9 +1664,9 @@
metric.set_use_zero_default_base(true);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
return true;
@@ -1690,9 +1677,9 @@
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
auto iter = valueProducer->mCurrentSlicedBucket.begin();
- auto& interval1 = iter->second[0];
+ auto& interval1 = iter->second.intervals[0];
auto iterBase = valueProducer->mCurrentBaseInfo.begin();
- auto& baseInfo1 = iterBase->second[0];
+ auto& baseInfo1 = iterBase->second.baseInfos[0];
EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
EXPECT_EQ(true, baseInfo1.hasBase);
EXPECT_EQ(3, baseInfo1.base.long_value);
@@ -1726,8 +1713,8 @@
}
EXPECT_TRUE(it != iter);
EXPECT_TRUE(itBase != iterBase);
- auto& interval2 = it->second[0];
- auto& baseInfo2 = itBase->second[0];
+ auto& interval2 = it->second.intervals[0];
+ auto& baseInfo2 = itBase->second.baseInfos[0];
EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
EXPECT_EQ(true, baseInfo2.hasBase);
EXPECT_EQ(4, baseInfo2.base.long_value);
@@ -1753,9 +1740,9 @@
metric.set_use_zero_default_base(true);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
return true;
@@ -1766,9 +1753,10 @@
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
const auto& it = valueProducer->mCurrentSlicedBucket.begin();
- ValueMetricProducer::Interval& interval1 = it->second[0];
+ ValueMetricProducer::Interval& interval1 = it->second.intervals[0];
ValueMetricProducer::BaseInfo& baseInfo1 =
- valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0];
+ valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())
+ ->second.baseInfos[0];
EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
EXPECT_EQ(true, baseInfo1.hasBase);
EXPECT_EQ(3, baseInfo1.base.long_value);
@@ -1795,9 +1783,10 @@
}
}
EXPECT_TRUE(it2 != it);
- ValueMetricProducer::Interval& interval2 = it2->second[0];
+ ValueMetricProducer::Interval& interval2 = it2->second.intervals[0];
ValueMetricProducer::BaseInfo& baseInfo2 =
- valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0];
+ valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())
+ ->second.baseInfos[0];
EXPECT_EQ(2, it2->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
EXPECT_EQ(true, baseInfo2.hasBase);
EXPECT_EQ(4, baseInfo2.base.long_value);
@@ -1826,14 +1815,16 @@
// Get new references now that entries have been deleted from the map
const auto& it3 = valueProducer->mCurrentSlicedBucket.begin();
const auto& it4 = std::next(valueProducer->mCurrentSlicedBucket.begin());
- ASSERT_EQ(it3->second.size(), 1);
- ASSERT_EQ(it4->second.size(), 1);
- ValueMetricProducer::Interval& interval3 = it3->second[0];
- ValueMetricProducer::Interval& interval4 = it4->second[0];
+ ASSERT_EQ(it3->second.intervals.size(), 1);
+ ASSERT_EQ(it4->second.intervals.size(), 1);
+ ValueMetricProducer::Interval& interval3 = it3->second.intervals[0];
+ ValueMetricProducer::Interval& interval4 = it4->second.intervals[0];
ValueMetricProducer::BaseInfo& baseInfo3 =
- valueProducer->mCurrentBaseInfo.find(it3->first.getDimensionKeyInWhat())->second[0];
+ valueProducer->mCurrentBaseInfo.find(it3->first.getDimensionKeyInWhat())
+ ->second.baseInfos[0];
ValueMetricProducer::BaseInfo& baseInfo4 =
- valueProducer->mCurrentBaseInfo.find(it4->first.getDimensionKeyInWhat())->second[0];
+ valueProducer->mCurrentBaseInfo.find(it4->first.getDimensionKeyInWhat())
+ ->second.baseInfos[0];
EXPECT_EQ(true, baseInfo3.hasBase);
EXPECT_EQ(5, baseInfo3.base.long_value);
@@ -1858,9 +1849,9 @@
metric.mutable_dimensions_in_what()->add_child()->set_field(1);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
return true;
@@ -1871,9 +1862,9 @@
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
auto iter = valueProducer->mCurrentSlicedBucket.begin();
- auto& interval1 = iter->second[0];
+ auto& interval1 = iter->second.intervals[0];
auto iterBase = valueProducer->mCurrentBaseInfo.begin();
- auto& baseInfo1 = iterBase->second[0];
+ auto& baseInfo1 = iterBase->second.baseInfos[0];
EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
EXPECT_EQ(true, baseInfo1.hasBase);
EXPECT_EQ(3, baseInfo1.base.long_value);
@@ -1909,8 +1900,8 @@
}
EXPECT_TRUE(it != iter);
EXPECT_TRUE(itBase != iterBase);
- auto interval2 = it->second[0];
- auto baseInfo2 = itBase->second[0];
+ auto interval2 = it->second.intervals[0];
+ auto baseInfo2 = itBase->second.baseInfos[0];
EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
EXPECT_EQ(true, baseInfo2.hasBase);
EXPECT_EQ(4, baseInfo2.base.long_value);
@@ -1923,8 +1914,8 @@
valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
// Only one interval left. One was trimmed.
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ interval2 = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
EXPECT_EQ(true, baseInfo2.hasBase);
EXPECT_EQ(5, baseInfo2.base.long_value);
@@ -1937,8 +1928,8 @@
allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 2, 14));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs);
- interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ interval2 = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, baseInfo2.hasBase);
EXPECT_EQ(14, baseInfo2.base.long_value);
EXPECT_EQ(false, interval2.hasValue);
@@ -1961,9 +1952,9 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
// Used by onConditionChanged.
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
return true;
@@ -1977,8 +1968,9 @@
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval& curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second[0];
- ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ValueMetricProducer::BaseInfo& curBaseInfo =
+ valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(100, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
@@ -1995,9 +1987,9 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // Condition change to true.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
@@ -2014,8 +2006,9 @@
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval& curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second[0];
- ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ValueMetricProducer::BaseInfo& curBaseInfo =
+ valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(100, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
@@ -2034,16 +2027,16 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 50));
return false;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1); // Condition change to false.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
@@ -2064,8 +2057,9 @@
valueProducer->onConditionChanged(false, bucketStartTimeNs + 1);
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval& curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second[0];
- ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ValueMetricProducer::BaseInfo curBaseInfo =
+ valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(false, curBaseInfo.hasBase);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(false, valueProducer->mHasGlobalBase);
@@ -2077,9 +2071,9 @@
metric.set_max_pull_delay_sec(0);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 120));
return true;
@@ -2097,19 +2091,15 @@
TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
ValueMetricProducer valueProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard,
- logEventMatcherIndex, eventMatcherWizard, tagId,
+ protoHash, logEventMatcherIndex, eventMatcherWizard, tagId,
bucket2StartTimeNs, bucket2StartTimeNs, pullerManager);
valueProducer.prepareFirstBucket();
valueProducer.mCondition = ConditionState::kFalse;
@@ -2124,9 +2114,9 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 100));
return true;
@@ -2141,8 +2131,9 @@
valueProducer->mHasGlobalBase = true;
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval& curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second[0];
- ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ValueMetricProducer::BaseInfo curBaseInfo =
+ valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(100, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
@@ -2156,12 +2147,12 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// First onConditionChanged
.WillOnce(Return(false))
// Second onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130));
@@ -2194,8 +2185,9 @@
// Contains base from last pull which was successful.
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval& curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second[0];
- ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ValueMetricProducer::BaseInfo curBaseInfo =
+ valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(140, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
@@ -2233,10 +2225,10 @@
metric.set_condition(StringToId("SCREEN_ON"));
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 2, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 2, _))
// First onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
for (int i = 0; i < 2000; i++) {
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, i));
}
@@ -2290,10 +2282,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// First onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 2);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 120));
@@ -2301,7 +2293,7 @@
}))
// Second onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130));
@@ -2332,8 +2324,9 @@
// Contains base from last pull which was successful.
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval& curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second[0];
- ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ValueMetricProducer::BaseInfo curBaseInfo =
+ valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(140, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
@@ -2369,10 +2362,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// First onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 2);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 120));
@@ -2380,7 +2373,7 @@
}))
// Second onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130));
@@ -2411,8 +2404,9 @@
// Last pull failed so base has been reset.
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval& curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second[0];
- ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ValueMetricProducer::BaseInfo curBaseInfo =
+ valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(false, curBaseInfo.hasBase);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(false, valueProducer->mHasGlobalBase);
@@ -2442,10 +2436,10 @@
TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
// Start bucket.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
return true;
@@ -2475,17 +2469,17 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// First onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
return true;
@@ -2498,8 +2492,9 @@
valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval& curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second[0];
- ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ValueMetricProducer::BaseInfo curBaseInfo =
+ valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(true, valueProducer->mHasGlobalBase);
@@ -2507,8 +2502,8 @@
// Empty pull.
valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(false, curBaseInfo.hasBase);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(false, valueProducer->mHasGlobalBase);
@@ -2518,24 +2513,24 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// First onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 11);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 2));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 12);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 5));
@@ -2551,8 +2546,9 @@
valueProducer->onConditionChanged(true, bucketStartTimeNs + 12);
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval& curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second[0];
- ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ValueMetricProducer::BaseInfo curBaseInfo =
+ valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(true, valueProducer->mHasGlobalBase);
@@ -2562,8 +2558,8 @@
allData.clear();
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
// Data is empty, base should be reset.
EXPECT_EQ(false, curBaseInfo.hasBase);
EXPECT_EQ(5, curBaseInfo.base.long_value);
@@ -2582,10 +2578,10 @@
metric.set_condition(StringToId("SCREEN_ON"));
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _))
// First onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
return true;
@@ -2608,14 +2604,14 @@
ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
auto iterator = valueProducer->mCurrentSlicedBucket.begin();
auto baseInfoIter = valueProducer->mCurrentBaseInfo.begin();
- EXPECT_EQ(true, baseInfoIter->second[0].hasBase);
- EXPECT_EQ(2, baseInfoIter->second[0].base.long_value);
- EXPECT_EQ(false, iterator->second[0].hasValue);
+ EXPECT_EQ(true, baseInfoIter->second.baseInfos[0].hasBase);
+ EXPECT_EQ(2, baseInfoIter->second.baseInfos[0].base.long_value);
+ EXPECT_EQ(false, iterator->second.intervals[0].hasValue);
iterator++;
baseInfoIter++;
- EXPECT_EQ(false, baseInfoIter->second[0].hasBase);
- EXPECT_EQ(1, baseInfoIter->second[0].base.long_value);
- EXPECT_EQ(false, iterator->second[0].hasValue);
+ EXPECT_EQ(false, baseInfoIter->second.baseInfos[0].hasBase);
+ EXPECT_EQ(1, baseInfoIter->second.baseInfos[0].base.long_value);
+ EXPECT_EQ(false, iterator->second.intervals[0].hasValue);
EXPECT_EQ(true, valueProducer->mHasGlobalBase);
}
@@ -2625,19 +2621,19 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
int64_t partialBucketSplitTimeNs = bucketStartTimeNs + bucketSizeNs / 2;
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Initialization.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
return true;
}))
// notifyAppUpgrade.
- .WillOnce(Invoke([partialBucketSplitTimeNs](
- int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
+ const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10));
@@ -2681,10 +2677,10 @@
TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Second onConditionChanged.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 5));
@@ -2692,7 +2688,7 @@
}))
// Third onConditionChanged.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket3StartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 10, 7));
@@ -2714,8 +2710,8 @@
valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(5, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
@@ -2752,10 +2748,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
// Initialization.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
return true;
@@ -2782,19 +2778,19 @@
int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2;
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Initialization.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
return true;
}))
// notifyAppUpgrade.
- .WillOnce(Invoke([partialBucketSplitTimeNs](
- int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
+ const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10));
@@ -2822,10 +2818,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// First on condition changed.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
@@ -2833,7 +2829,7 @@
}))
// Second on condition changed.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
@@ -2849,8 +2845,8 @@
valueProducer->onConditionChanged(false, bucketStartTimeNs + 12);
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(2, curInterval.value.long_value);
@@ -2867,10 +2863,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// First condition change.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
@@ -2878,7 +2874,7 @@
}))
// 2nd condition change.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 8);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 1));
@@ -2886,7 +2882,7 @@
}))
// 3rd condition change.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 1));
@@ -2920,29 +2916,25 @@
TEST(ValueMetricProducerTest, TestPullNeededFastDump) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
// Initial pull.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
return true;
}));
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
- eventMatcherWizard, tagId, bucketStartTimeNs,
- bucketStartTimeNs, pullerManager);
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+ logEventMatcherIndex, eventMatcherWizard, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
valueProducer.prepareFirstBucket();
ProtoOutputStream output;
@@ -2958,29 +2950,25 @@
TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
// Initial pull.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
return true;
}));
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
- eventMatcherWizard, tagId, bucketStartTimeNs,
- bucketStartTimeNs, pullerManager);
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+ logEventMatcherIndex, eventMatcherWizard, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
valueProducer.prepareFirstBucket();
vector<shared_ptr<LogEvent>> allData;
@@ -3002,28 +2990,24 @@
TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- UidMap uidMap;
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
sp<EventMatcherWizard> eventMatcherWizard =
- new EventMatcherWizard({new SimpleLogMatchingTracker(
- atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Initial pull.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(
@@ -3031,9 +3015,9 @@
return true;
}));
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
- eventMatcherWizard, tagId, bucketStartTimeNs,
- bucketStartTimeNs, pullerManager);
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+ logEventMatcherIndex, eventMatcherWizard, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
valueProducer.prepareFirstBucket();
ProtoOutputStream output;
@@ -3069,10 +3053,10 @@
metric.set_use_diff(false);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// condition becomes true
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
@@ -3080,7 +3064,7 @@
}))
// condition becomes false
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 20));
@@ -3095,8 +3079,9 @@
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second[0];
- ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ValueMetricProducer::BaseInfo curBaseInfo =
+ valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(false, curBaseInfo.hasBase);
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(20, curInterval.value.long_value);
@@ -3108,8 +3093,8 @@
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {50 - 8},
{bucketStartTimeNs}, {bucket2StartTimeNs});
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(false, curBaseInfo.hasBase);
EXPECT_EQ(false, curInterval.hasValue);
}
@@ -3119,10 +3104,10 @@
metric.set_use_diff(false);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _))
// condition becomes true
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
return true;
@@ -3141,8 +3126,9 @@
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8},
{bucketStartTimeNs}, {bucket2StartTimeNs});
ValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second[0];
- ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ValueMetricProducer::BaseInfo curBaseInfo =
+ valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
EXPECT_EQ(false, curBaseInfo.hasBase);
EXPECT_EQ(false, curInterval.hasValue);
}
@@ -3170,10 +3156,10 @@
metric.set_use_diff(false);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// condition becomes true
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
@@ -3210,10 +3196,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 20, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 20, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20, 10));
return true;
@@ -3256,10 +3242,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 50, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 50, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
return true;
@@ -3314,10 +3300,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
@@ -3325,7 +3311,7 @@
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 100);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 100, 15));
@@ -3380,10 +3366,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
@@ -3391,7 +3377,7 @@
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10000);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 100, 15));
@@ -3436,10 +3422,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
@@ -3486,10 +3472,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
@@ -3497,7 +3483,7 @@
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket4StartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1000, 15));
@@ -3560,10 +3546,10 @@
metric.set_min_bucket_size_nanos(10000000000); // 10 seconds
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
@@ -3571,7 +3557,7 @@
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 9000000);
data->clear();
data->push_back(
@@ -3651,10 +3637,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10 * NS_PER_SEC);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(
@@ -3663,7 +3649,7 @@
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 15 * NS_PER_SEC);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(
@@ -3740,10 +3726,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
@@ -3751,7 +3737,7 @@
}))
// App Update.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1000);
data->clear();
data->push_back(
@@ -3806,10 +3792,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
return true;
@@ -3857,10 +3843,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// First condition change event.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
for (int i = 0; i < 2000; i++) {
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, i));
@@ -3877,7 +3863,7 @@
.WillOnce(Return(false))
.WillOnce(Return(false))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 220);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 220, 10));
@@ -3976,10 +3962,10 @@
// Set up ValueMetricProducer.
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE");
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// ValueMetricProducer initialized.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
@@ -3987,34 +3973,38 @@
}))
// Screen state change to ON.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5);
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5, 5));
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5 * NS_PER_SEC, 5));
return true;
}))
// Screen state change to OFF.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 9));
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 9));
return true;
}))
// Screen state change to ON.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15);
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 15, 21));
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 15 * NS_PER_SEC, 21));
return true;
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 30));
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 30));
return true;
}));
@@ -4034,138 +4024,174 @@
// Base for dimension key {}
auto it = valueProducer->mCurrentSlicedBucket.begin();
auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second[0].hasBase);
- EXPECT_EQ(3, itBase->second[0].base.long_value);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- itBase->second[0].currentState.getValues()[0].mValue.int_value);
+ itBase->second.currentState.getValues()[0].mValue.int_value);
// Value for dimension, state key {{}, kStateUnknown}
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second[0].hasValue);
+ EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
// Bucket status after screen state change kStateUnknown->ON.
auto screenEvent = CreateScreenStateChangedEvent(
- bucketStartTimeNs + 5, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {}
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second[0].hasBase);
- EXPECT_EQ(5, itBase->second[0].base.long_value);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
- itBase->second[0].currentState.getValues()[0].mValue.int_value);
- // Value for dimension, state key {{}, kStateUnknown}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second[0].hasValue);
- EXPECT_EQ(2, it->second[0].value.long_value);
-
- // Bucket status after screen state change ON->OFF.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10,
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
+ bucketStartTimeNs + 5 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
StateManager::getInstance().onLogEvent(*screenEvent);
ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second[0].hasBase);
- EXPECT_EQ(9, itBase->second[0].base.long_value);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
- itBase->second[0].currentState.getValues()[0].mValue.int_value);
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
// Value for dimension, state key {{}, ON}
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second[0].hasValue);
- EXPECT_EQ(4, it->second[0].value.long_value);
+ EXPECT_EQ(0, it->second.intervals.size());
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
// Value for dimension, state key {{}, kStateUnknown}
it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second[0].hasValue);
- EXPECT_EQ(2, it->second[0].value.long_value);
+ EXPECT_TRUE(it->second.intervals[0].hasValue);
+ EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
+
+ // Bucket status after screen state change ON->OFF.
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
+ StateManager::getInstance().onLogEvent(*screenEvent);
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {}
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, OFF}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_EQ(0, it->second.intervals.size());
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+ // Value for dimension, state key {{}, ON}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_TRUE(it->second.intervals[0].hasValue);
+ EXPECT_EQ(4, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 10 * NS_PER_SEC);
+ // Value for dimension, state key {{}, kStateUnknown}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_TRUE(it->second.intervals[0].hasValue);
+ EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
// Bucket status after screen state change OFF->ON.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15,
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15 * NS_PER_SEC,
android::view::DisplayStateEnum::DISPLAY_STATE_ON);
StateManager::getInstance().onLogEvent(*screenEvent);
ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second[0].hasBase);
- EXPECT_EQ(21, itBase->second[0].base.long_value);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(21, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
- itBase->second[0].currentState.getValues()[0].mValue.int_value);
+ itBase->second.currentState.getValues()[0].mValue.int_value);
// Value for dimension, state key {{}, OFF}
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second[0].hasValue);
- EXPECT_EQ(12, it->second[0].value.long_value);
+ EXPECT_TRUE(it->second.intervals[0].hasValue);
+ EXPECT_EQ(12, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 15 * NS_PER_SEC);
// Value for dimension, state key {{}, ON}
it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second[0].hasValue);
- EXPECT_EQ(4, it->second[0].value.long_value);
+ EXPECT_TRUE(it->second.intervals[0].hasValue);
+ EXPECT_EQ(4, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, true, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 15 * NS_PER_SEC);
// Value for dimension, state key {{}, kStateUnknown}
it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second[0].hasValue);
- EXPECT_EQ(2, it->second[0].value.long_value);
+ EXPECT_TRUE(it->second.intervals[0].hasValue);
+ EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
// Start dump report and check output.
ProtoOutputStream output;
std::set<string> strSet;
- valueProducer->onDumpReport(bucketStartTimeNs + 50, true /* include recent buckets */, true,
- NO_TIME_CONSTRAINTS, &strSet, &output);
+ valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
StatsLogReport report = outputStreamToProto(&output);
EXPECT_TRUE(report.has_value_metrics());
ASSERT_EQ(3, report.value_metrics().data_size());
+ // {{}, kStateUnknown}
auto data = report.value_metrics().data(0);
ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value());
+ EXPECT_EQ(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {{}, ON}
data = report.value_metrics().data(1);
ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size());
EXPECT_EQ(13, report.value_metrics().data(1).bucket_info(0).values(0).value_long());
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value());
+ EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {{}, OFF}
data = report.value_metrics().data(2);
ASSERT_EQ(1, report.value_metrics().data(2).bucket_info_size());
EXPECT_EQ(12, report.value_metrics().data(2).bucket_info(0).values(0).value_long());
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value());
+ EXPECT_EQ(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
}
/*
@@ -4178,10 +4204,10 @@
// Set up ValueMetricProducer.
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE_ONOFF");
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// ValueMetricProducer initialized.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
@@ -4189,10 +4215,11 @@
}))
// Screen state change to ON.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5);
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5, 5));
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5 * NS_PER_SEC, 5));
return true;
}))
// Screen state change to VR has no pull because it is in the same
@@ -4203,18 +4230,20 @@
// Screen state change to OFF.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15);
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 15, 21));
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 15 * NS_PER_SEC, 21));
return true;
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 30));
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 30));
return true;
}));
@@ -4245,147 +4274,193 @@
// Base for dimension key {}
auto it = valueProducer->mCurrentSlicedBucket.begin();
auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second[0].hasBase);
- EXPECT_EQ(3, itBase->second[0].base.long_value);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- itBase->second[0].currentState.getValues()[0].mValue.int_value);
+ itBase->second.currentState.getValues()[0].mValue.int_value);
// Value for dimension, state key {{}, {kStateUnknown}}
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second[0].hasValue);
+ EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
// Bucket status after screen state change kStateUnknown->ON.
auto screenEvent = CreateScreenStateChangedEvent(
- bucketStartTimeNs + 5, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {}
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second[0].hasBase);
- EXPECT_EQ(5, itBase->second[0].base.long_value);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
- EXPECT_EQ(screenOnGroup.group_id(),
- itBase->second[0].currentState.getValues()[0].mValue.long_value);
- // Value for dimension, state key {{}, kStateUnknown}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second[0].hasValue);
- EXPECT_EQ(2, it->second[0].value.long_value);
-
- // Bucket status after screen state change ON->VR.
- // Both ON and VR are in the same state group, so the base should not change.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10,
- android::view::DisplayStateEnum::DISPLAY_STATE_VR);
- StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {}
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second[0].hasBase);
- EXPECT_EQ(5, itBase->second[0].base.long_value);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
- EXPECT_EQ(screenOnGroup.group_id(),
- itBase->second[0].currentState.getValues()[0].mValue.int_value);
- // Value for dimension, state key {{}, kStateUnknown}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second[0].hasValue);
- EXPECT_EQ(2, it->second[0].value.long_value);
-
- // Bucket status after screen state change VR->ON.
- // Both ON and VR are in the same state group, so the base should not change.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 12,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {}
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second[0].hasBase);
- EXPECT_EQ(5, itBase->second[0].base.long_value);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
- EXPECT_EQ(screenOnGroup.group_id(),
- itBase->second[0].currentState.getValues()[0].mValue.int_value);
- // Value for dimension, state key {{}, kStateUnknown}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second[0].hasValue);
- EXPECT_EQ(2, it->second[0].value.long_value);
-
- // Bucket status after screen state change VR->OFF.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15,
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
+ bucketStartTimeNs + 5 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
StateManager::getInstance().onLogEvent(*screenEvent);
ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second[0].hasBase);
- EXPECT_EQ(21, itBase->second[0].base.long_value);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
- EXPECT_EQ(screenOffGroup.group_id(),
- itBase->second[0].currentState.getValues()[0].mValue.int_value);
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(screenOnGroup.group_id(),
+ itBase->second.currentState.getValues()[0].mValue.long_value);
// Value for dimension, state key {{}, ON GROUP}
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(screenOnGroup.group_id(),
- it->first.getStateValuesKey().getValues()[0].mValue.long_value);
- EXPECT_TRUE(it->second[0].hasValue);
- EXPECT_EQ(16, it->second[0].value.long_value);
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
// Value for dimension, state key {{}, kStateUnknown}
it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second[0].hasValue);
- EXPECT_EQ(2, it->second[0].value.long_value);
+ EXPECT_TRUE(it->second.intervals[0].hasValue);
+ EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
+
+ // Bucket status after screen state change ON->VR.
+ // Both ON and VR are in the same state group, so the base should not change.
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_VR);
+ StateManager::getInstance().onLogEvent(*screenEvent);
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {}
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(screenOnGroup.group_id(),
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, ON GROUP}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(screenOnGroup.group_id(),
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
+ // Value for dimension, state key {{}, kStateUnknown}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_TRUE(it->second.intervals[0].hasValue);
+ EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
+
+ // Bucket status after screen state change VR->ON.
+ // Both ON and VR are in the same state group, so the base should not change.
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 12 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+ StateManager::getInstance().onLogEvent(*screenEvent);
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {}
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(screenOnGroup.group_id(),
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, ON GROUP}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(screenOnGroup.group_id(),
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
+ // Value for dimension, state key {{}, kStateUnknown}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_TRUE(it->second.intervals[0].hasValue);
+ EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
+
+ // Bucket status after screen state change VR->OFF.
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
+ StateManager::getInstance().onLogEvent(*screenEvent);
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {}
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(21, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(screenOffGroup.group_id(),
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, OFF GROUP}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(screenOffGroup.group_id(),
+ it->first.getStateValuesKey().getValues()[0].mValue.long_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 15 * NS_PER_SEC);
+ // Value for dimension, state key {{}, ON GROUP}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(screenOnGroup.group_id(),
+ it->first.getStateValuesKey().getValues()[0].mValue.long_value);
+ EXPECT_TRUE(it->second.intervals[0].hasValue);
+ EXPECT_EQ(16, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 15 * NS_PER_SEC);
+ // Value for dimension, state key {{}, kStateUnknown}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_TRUE(it->second.intervals[0].hasValue);
+ EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
// Start dump report and check output.
ProtoOutputStream output;
std::set<string> strSet;
- valueProducer->onDumpReport(bucketStartTimeNs + 50, true /* include recent buckets */, true,
- NO_TIME_CONSTRAINTS, &strSet, &output);
+ valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
StatsLogReport report = outputStreamToProto(&output);
EXPECT_TRUE(report.has_value_metrics());
ASSERT_EQ(3, report.value_metrics().data_size());
+ // {{}, kStateUnknown}
auto data = report.value_metrics().data(0);
ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value());
+ EXPECT_EQ(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {{}, ON GROUP}
data = report.value_metrics().data(1);
ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size());
EXPECT_EQ(16, report.value_metrics().data(1).bucket_info(0).values(0).value_long());
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_group_id());
EXPECT_EQ(screenOnGroup.group_id(), data.slice_by_state(0).group_id());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {{}, OFF GROUP}
data = report.value_metrics().data(2);
ASSERT_EQ(1, report.value_metrics().data(2).bucket_info_size());
EXPECT_EQ(9, report.value_metrics().data(2).bucket_info(0).values(0).value_long());
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_group_id());
EXPECT_EQ(screenOffGroup.group_id(), data.slice_by_state(0).group_id());
+ EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
}
/*
@@ -4407,11 +4482,40 @@
auto fieldsInState = stateLink->mutable_fields_in_state();
*fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
+ /*
+ NOTE: "1" denotes uid 1 and "2" denotes uid 2.
+ bucket # 1 bucket # 2
+ 10 20 30 40 50 60 70 80 90 100 110 120 (seconds)
+ |------------------------------------------|---------------------------------|--
+
+ (kStateUnknown)
+ 1
+ |-------------|
+ 20
+
+ 2
+ |----------------------------|
+ 40
+
+ (FOREGROUND)
+ 1 1
+ |----------------------------|-------------| |------|
+ 40 20 10
+
+
+ (BACKGROUND)
+ 1
+ |------------|
+ 20
+ 2
+ |-------------|---------------------------------|
+ 20 50
+ */
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// ValueMetricProducer initialized.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 2 /*uid*/, 7));
@@ -4420,65 +4524,65 @@
}))
// Uid 1 process state change from kStateUnknown -> Foreground
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
data->clear();
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20, 1 /*uid*/, 6));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+ 1 /*uid*/, 6));
// This event should be skipped.
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20, 2 /*uid*/, 8));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+ 2 /*uid*/, 8));
return true;
}))
// Uid 2 process state change from kStateUnknown -> Background
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40);
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
data->clear();
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40, 2 /*uid*/, 9));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+ 2 /*uid*/, 9));
// This event should be skipped.
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40, 1 /*uid*/, 12));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+ 1 /*uid*/, 12));
return true;
}))
// Uid 1 process state change from Foreground -> Background
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 20);
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 20 * NS_PER_SEC);
data->clear();
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20, 1 /*uid*/, 13));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20 * NS_PER_SEC,
+ 1 /*uid*/, 13));
// This event should be skipped.
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20, 2 /*uid*/, 11));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20 * NS_PER_SEC,
+ 2 /*uid*/, 11));
return true;
}))
// Uid 1 process state change from Background -> Foreground
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 40);
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 40 * NS_PER_SEC);
data->clear();
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40, 1 /*uid*/, 17));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40 * NS_PER_SEC,
+ 1 /*uid*/, 17));
// This event should be skipped.
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40, 2 /*uid */, 15));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40 * NS_PER_SEC,
+ 2 /*uid */, 15));
return true;
}))
// Dump report pull.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50);
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC);
data->clear();
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50, 2 /*uid*/, 20));
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50, 1 /*uid*/, 21));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50 * NS_PER_SEC,
+ 2 /*uid*/, 20));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50 * NS_PER_SEC,
+ 1 /*uid*/, 21));
return true;
}));
@@ -4497,113 +4601,153 @@
// Base for dimension key {uid 1}.
auto it = valueProducer->mCurrentSlicedBucket.begin();
auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second[0].hasBase);
- EXPECT_EQ(3, itBase->second[0].base.long_value);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- itBase->second[0].currentState.getValues()[0].mValue.int_value);
+ itBase->second.currentState.getValues()[0].mValue.int_value);
// Value for dimension, state key {{uid 1}, kStateUnknown}
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second[0].hasValue);
+ EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
// Base for dimension key {uid 2}
it++;
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second[0].hasBase);
- EXPECT_EQ(7, itBase->second[0].base.long_value);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(7, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- itBase->second[0].currentState.getValues()[0].mValue.int_value);
+ itBase->second.currentState.getValues()[0].mValue.int_value);
// Value for dimension, state key {{uid 2}, kStateUnknown}
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second[0].hasValue);
+ EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
// Bucket status after uid 1 process state change kStateUnknown -> Foreground.
- auto uidProcessEvent = CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 20, 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
+ auto uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
StateManager::getInstance().onLogEvent(*uidProcessEvent);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
// Base for dimension key {uid 1}.
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second[0].hasBase);
- EXPECT_EQ(6, itBase->second[0].base.long_value);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(6, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- itBase->second[0].currentState.getValues()[0].mValue.int_value);
+ itBase->second.currentState.getValues()[0].mValue.int_value);
// Value for key {uid 1, kStateUnknown}.
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second[0].hasValue);
- EXPECT_EQ(3, it->second[0].value.long_value);
+ EXPECT_TRUE(it->second.intervals[0].hasValue);
+ EXPECT_EQ(3, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+ // Value for key {uid 1, FOREGROUND}.
+ it++;
+ ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
- // Base for dimension key {uid 2}
+ // Base for dimension key {uid 2}.
it++;
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second[0].hasBase);
- EXPECT_EQ(7, itBase->second[0].base.long_value);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(7, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- itBase->second[0].currentState.getValues()[0].mValue.int_value);
- // Value for key {uid 2, kStateUnknown}
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {uid 2, kStateUnknown}.
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second[0].hasValue);
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
// Bucket status after uid 2 process state change kStateUnknown -> Background.
- uidProcessEvent = CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 40, 2 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucketStartTimeNs + 40 * NS_PER_SEC, 2 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
StateManager::getInstance().onLogEvent(*uidProcessEvent);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {uid 1}.
+ ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {uid 2}.
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second[0].hasBase);
- EXPECT_EQ(6, itBase->second[0].base.long_value);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {uid 2, BACKGROUND}.
+ ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 40 * NS_PER_SEC);
+
+ // Base for dimension key {uid 1}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(6, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- itBase->second[0].currentState.getValues()[0].mValue.int_value);
+ itBase->second.currentState.getValues()[0].mValue.int_value);
// Value for key {uid 1, kStateUnknown}.
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second[0].hasValue);
- EXPECT_EQ(3, it->second[0].value.long_value);
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_TRUE(it->second.intervals[0].hasValue);
+ EXPECT_EQ(3, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
- // Base for dimension key {uid 2}
+ // Value for key {uid 1, FOREGROUND}.
it++;
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second[0].hasBase);
- EXPECT_EQ(9, itBase->second[0].base.long_value);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second[0].currentState.getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
// Value for key {uid 2, kStateUnknown}
+ it++;
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second[0].hasValue);
- EXPECT_EQ(2, it->second[0].value.long_value);
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_TRUE(it->second.intervals[0].hasValue);
+ EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 40 * NS_PER_SEC,
+ bucketStartTimeNs + 40 * NS_PER_SEC);
// Pull at end of first bucket.
vector<shared_ptr<LogEvent>> allData;
@@ -4620,36 +4764,40 @@
it = valueProducer->mCurrentSlicedBucket.begin();
EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second[0].hasBase);
- EXPECT_EQ(15, itBase->second[0].base.long_value);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(15, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second[0].currentState.getValues()[0].mValue.int_value);
+ itBase->second.currentState.getValues()[0].mValue.int_value);
// Value for key {uid 2, BACKGROUND}.
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second[0].hasValue);
+ EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+ EXPECT_EQ(20 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
// Base for dimension key {uid 1}
it++;
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second[0].hasBase);
- EXPECT_EQ(10, itBase->second[0].base.long_value);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(10, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- itBase->second[0].currentState.getValues()[0].mValue.int_value);
+ itBase->second.currentState.getValues()[0].mValue.int_value);
// Value for key {uid 1, kStateUnknown}
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* kStateTracker::kUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second[0].hasValue);
+ EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+ EXPECT_EQ(20 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
// Value for key {uid 1, FOREGROUND}
it++;
@@ -4658,7 +4806,9 @@
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second[0].hasValue);
+ EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+ EXPECT_EQ(40 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
// Value for key {uid 2, kStateUnknown}
it++;
@@ -4667,106 +4817,54 @@
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* kStateTracker::kUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second[0].hasValue);
+ EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 40 * NS_PER_SEC);
+ EXPECT_EQ(40 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
// Bucket status after uid 1 process state change from Foreground -> Background.
- uidProcessEvent = CreateUidProcessStateChangedEvent(
- bucket2StartTimeNs + 20, 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 20 * NS_PER_SEC, 1 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
StateManager::getInstance().onLogEvent(*uidProcessEvent);
- ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size());
ASSERT_EQ(4UL, valueProducer->mPastBuckets.size());
ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
// Base for dimension key {uid 2}.
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second[0].hasBase);
- EXPECT_EQ(15, itBase->second[0].base.long_value);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(15, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second[0].currentState.getValues()[0].mValue.int_value);
+ itBase->second.currentState.getValues()[0].mValue.int_value);
// Value for key {uid 2, BACKGROUND}.
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second[0].hasValue);
+ EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+
// Base for dimension key {uid 1}
it++;
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second[0].hasBase);
- EXPECT_EQ(13, itBase->second[0].base.long_value);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(13, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second[0].currentState.getValues()[0].mValue.int_value);
+ itBase->second.currentState.getValues()[0].mValue.int_value);
// Value for key {uid 1, kStateUnknown}
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second[0].hasValue);
- // Value for key {uid 1, FOREGROUND}
- it++;
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second[0].hasValue);
- EXPECT_EQ(3, it->second[0].value.long_value);
- // Value for key {uid 2, kStateUnknown}
- it++;
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second[0].hasValue);
-
- // Bucket status after uid 1 process state change Background->Foreground.
- uidProcessEvent = CreateUidProcessStateChangedEvent(
- bucket2StartTimeNs + 40, 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
- StateManager::getInstance().onLogEvent(*uidProcessEvent);
-
- ASSERT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size());
- ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
- // Base for dimension key {uid 2}
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second[0].hasBase);
- EXPECT_EQ(15, itBase->second[0].base.long_value);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second[0].currentState.getValues()[0].mValue.int_value);
- // Value for key {uid 2, BACKGROUND}
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second[0].hasValue);
-
- // Base for dimension key {uid 1}
- it++;
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second[0].hasBase);
- EXPECT_EQ(17, itBase->second[0].base.long_value);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- itBase->second[0].currentState.getValues()[0].mValue.int_value);
- // Value for key {uid 1, kStateUnknown}
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second[0].hasValue);
+ EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
// Value for key {uid 1, BACKGROUND}
it++;
@@ -4775,8 +4873,7 @@
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second[0].hasValue);
- EXPECT_EQ(4, it->second[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 20 * NS_PER_SEC);
// Value for key {uid 1, FOREGROUND}
it++;
@@ -4785,8 +4882,10 @@
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second[0].hasValue);
- EXPECT_EQ(3, it->second[0].value.long_value);
+ EXPECT_TRUE(it->second.intervals[0].hasValue);
+ EXPECT_EQ(3, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucket2StartTimeNs + 20 * NS_PER_SEC);
// Value for key {uid 2, kStateUnknown}
it++;
@@ -4795,17 +4894,98 @@
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 40 * NS_PER_SEC);
+
+ // Bucket status after uid 1 process state change Background->Foreground.
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 40 * NS_PER_SEC, 1 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
+ StateManager::getInstance().onLogEvent(*uidProcessEvent);
+
+ ASSERT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
+ // Base for dimension key {uid 2}
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(15, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {uid 2, BACKGROUND}
+ ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+
+ // Base for dimension key {uid 1}
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(17, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {uid 1, kStateUnknown}
+ ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {uid 1, BACKGROUND}
+ it++;
+ ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_TRUE(it->second.intervals[0].hasValue);
+ EXPECT_EQ(4, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucket2StartTimeNs + 40 * NS_PER_SEC);
+
+ // Value for key {uid 1, FOREGROUND}
+ it++;
+ ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_TRUE(it->second.intervals[0].hasValue);
+ EXPECT_EQ(3, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
+ bucket2StartTimeNs + 40 * NS_PER_SEC);
+
+ // Value for key {uid 2, kStateUnknown}
+ it++;
+ ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 40 * NS_PER_SEC);
// Start dump report and check output.
ProtoOutputStream output;
std::set<string> strSet;
- valueProducer->onDumpReport(bucket2StartTimeNs + 50, true /* include recent buckets */, true,
- NO_TIME_CONSTRAINTS, &strSet, &output);
+ valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
StatsLogReport report = outputStreamToProto(&output);
EXPECT_TRUE(report.has_value_metrics());
ASSERT_EQ(5, report.value_metrics().data_size());
+ // {uid 1, BACKGROUND}
auto data = report.value_metrics().data(0);
ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(4, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
@@ -4813,14 +4993,18 @@
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
data.slice_by_state(0).value());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {uid 2, kStateUnknown}
data = report.value_metrics().data(1);
ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size());
EXPECT_EQ(2, report.value_metrics().data(1).bucket_info(0).values(0).value_long());
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value());
+ EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {uid 1, FOREGROUND}
data = report.value_metrics().data(2);
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
@@ -4829,14 +5013,19 @@
ASSERT_EQ(2, report.value_metrics().data(2).bucket_info_size());
EXPECT_EQ(4, report.value_metrics().data(2).bucket_info(0).values(0).value_long());
EXPECT_EQ(7, report.value_metrics().data(2).bucket_info(1).values(0).value_long());
+ EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+ // {uid 1, kStateUnknown}
data = report.value_metrics().data(3);
ASSERT_EQ(1, report.value_metrics().data(3).bucket_info_size());
EXPECT_EQ(3, report.value_metrics().data(3).bucket_info(0).values(0).value_long());
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {uid 2, BACKGROUND}
data = report.value_metrics().data(4);
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
@@ -4845,6 +5034,1630 @@
ASSERT_EQ(2, report.value_metrics().data(4).bucket_info_size());
EXPECT_EQ(6, report.value_metrics().data(4).bucket_info(0).values(0).value_long());
EXPECT_EQ(5, report.value_metrics().data(4).bucket_info(1).values(0).value_long());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+}
+
+/*
+ * Test slicing condition_true_nanos by state for metric that slices by state when data is not
+ * present in pulled data during a state change.
+ */
+TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataInStateChange) {
+ // Set up ValueMetricProducer.
+ ValueMetric metric =
+ ValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE");
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ /*
+ NOTE: "-" means that the data was not present in the pulled data.
+
+ bucket # 1
+ 10 20 30 40 50 60 (seconds)
+ |-------------------------------------------------------|--
+ x (kStateUnknown)
+ |-----------|
+ 10
+
+ x x (ON)
+ |---------------------| |-----------|
+ 20 10
+
+ - (OFF)
+ */
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ // ValueMetricProducer initialized.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
+ return true;
+ }))
+ // Battery saver mode state changed to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 5));
+ return true;
+ }))
+ // Battery saver mode state changed to OFF but data for dimension key {} is not present
+ // in the pulled data.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC);
+ data->clear();
+ return true;
+ }))
+ // Battery saver mode state changed to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC, 7));
+ return true;
+ }))
+ // Dump report pull.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 15));
+ return true;
+ }));
+
+ StateManager::getInstance().clear();
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithState(
+ pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {});
+ EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
+
+ // Set up StateManager and check that StateTrackers are initialized.
+ StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
+ valueProducer);
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
+ util::BATTERY_SAVER_MODE_STATE_CHANGED));
+
+ // Bucket status after metric initialized.
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {}
+ auto it = valueProducer->mCurrentSlicedBucket.begin();
+ auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, kStateUnknown}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
+
+ // Bucket status after battery saver mode ON event.
+ unique_ptr<LogEvent> batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+ // Base for dimension key {}
+
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Bucket status after battery saver mode OFF event which is not present
+ // in the pulled data.
+ unique_ptr<LogEvent> batterySaverOffEvent =
+ CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 30 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOffEvent);
+
+ // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_FALSE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Bucket status after battery saver mode ON event.
+ batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 40 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+ // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 40 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Start dump report and check output.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ ASSERT_EQ(2, report.value_metrics().data_size());
+
+ // {{}, kStateUnknown}
+ ValueMetricData data = report.value_metrics().data(0);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+ // {{}, ON}
+ data = report.value_metrics().data(1);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+}
+
+/*
+ * Test for metric that slices by state when data is not present in pulled data
+ * during an event and then a flush occurs for the current bucket. With the new
+ * condition timer behavior, a "new" MetricDimensionKey is inserted into
+ * `mCurrentSlicedBucket` before intervals are closed/added to that new
+ * MetricDimensionKey.
+ */
+TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataThenFlushBucket) {
+ // Set up ValueMetricProducer.
+ ValueMetric metric =
+ ValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE");
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ /*
+ NOTE: "-" means that the data was not present in the pulled data.
+
+ bucket # 1
+ 10 20 30 40 50 60 (seconds)
+ |-------------------------------------------------------|--
+ - (kStateUnknown)
+
+ - (ON)
+ */
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ // ValueMetricProducer initialized but data for dimension key {} is not present
+ // in the pulled data..
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
+ data->clear();
+ return true;
+ }))
+ // Battery saver mode state changed to ON but data for dimension key {} is not present
+ // in the pulled data.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+ data->clear();
+ return true;
+ }))
+ // Dump report pull.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 15));
+ return true;
+ }));
+
+ StateManager::getInstance().clear();
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithState(
+ pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {});
+ EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
+
+ // Set up StateManager and check that StateTrackers are initialized.
+ StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
+ valueProducer);
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
+ util::BATTERY_SAVER_MODE_STATE_CHANGED));
+
+ // Bucket status after metric initialized.
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer->mCurrentBaseInfo.size());
+
+ // Bucket status after battery saver mode ON event which is not present
+ // in the pulled data.
+ unique_ptr<LogEvent> batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+ ASSERT_EQ(0UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+
+ // Start dump report and check output.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ ASSERT_EQ(0, report.value_metrics().data_size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+}
+
+TEST(ValueMetricProducerTest, TestSlicedStateWithNoPullOnBucketBoundary) {
+ // Set up ValueMetricProducer.
+ ValueMetric metric =
+ ValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE");
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ /*
+ bucket # 1 bucket # 2
+ 10 20 30 40 50 60 70 80 90 100 110 120 (seconds)
+ |------------------------------------|---------------------------|--
+ x (kStateUnknown)
+ |-----|
+ 10
+ x x (ON)
+ |-----| |-----------|
+ 10 20
+ x (OFF)
+ |------------------------|
+ 40
+ */
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ // ValueMetricProducer initialized.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
+ return true;
+ }))
+ // Battery saver mode state changed to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 5));
+ return true;
+ }))
+ // Battery saver mode state changed to OFF.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, 7));
+ return true;
+ }))
+ // Battery saver mode state changed to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 30 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 10));
+ return true;
+ }))
+ // Dump report pull.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 15));
+ return true;
+ }));
+
+ StateManager::getInstance().clear();
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithState(
+ pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {});
+ EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
+
+ // Set up StateManager and check that StateTrackers are initialized.
+ StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
+ valueProducer);
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
+ util::BATTERY_SAVER_MODE_STATE_CHANGED));
+
+ // Bucket status after metric initialized.
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ auto it = valueProducer->mCurrentSlicedBucket.begin();
+ auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, kStateUnknown}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
+
+ // Bucket status after battery saver mode ON event.
+ unique_ptr<LogEvent> batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Bucket status after battery saver mode OFF event.
+ unique_ptr<LogEvent> batterySaverOffEvent =
+ CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 20 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOffEvent);
+
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::OFF,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, OFF}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::OFF,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{}, ON}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Bucket status after battery saver mode ON event.
+ batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucket2StartTimeNs + 30 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, OFF}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::OFF,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 30 * NS_PER_SEC,
+ bucket2StartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{}, ON}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Start dump report and check output.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ ASSERT_EQ(3, report.value_metrics().data_size());
+
+ // {{}, kStateUnknown}
+ ValueMetricData data = report.value_metrics().data(0);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+ // {{}, ON}
+ data = report.value_metrics().data(1);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+
+ // {{}, OFF}
+ data = report.value_metrics().data(2);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(BatterySaverModeStateChanged::OFF, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+}
+
+/*
+ * Test slicing condition_true_nanos by state for metric that slices by state when data is not
+ * present in pulled data during a condition change.
+ */
+TEST(ValueMetricProducerTest, TestSlicedStateWithDataMissingInConditionChange) {
+ // Set up ValueMetricProducer.
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithConditionAndState(
+ "BATTERY_SAVER_MODE_STATE");
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ /*
+ NOTE: "-" means that the data was not present in the pulled data.
+
+ bucket # 1
+ 10 20 30 40 50 60 (seconds)
+ |-------------------------------------------------------|--
+
+ T F T (Condition)
+ x (ON)
+ |----------------------| -
+ 20
+ */
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ // Battery saver mode state changed to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 3));
+ return true;
+ }))
+ // Condition changed to false.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30 * NS_PER_SEC, 5));
+ return true;
+ }))
+ // Condition changed to true but data for dimension key {} is not present in the
+ // pulled data.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
+ data->clear();
+ return true;
+ }))
+ // Dump report pull.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 20));
+ return true;
+ }));
+
+ StateManager::getInstance().clear();
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithConditionAndState(
+ pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {},
+ ConditionState::kTrue);
+ EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
+
+ // Set up StateManager and check that StateTrackers are initialized.
+ StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
+ valueProducer);
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
+ util::BATTERY_SAVER_MODE_STATE_CHANGED));
+
+ // Bucket status after battery saver mode ON event.
+ unique_ptr<LogEvent> batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+ // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ auto it = valueProducer->mCurrentSlicedBucket.begin();
+ auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket status after condition change to false.
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 30 * NS_PER_SEC);
+ // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket status after condition change to true.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 40 * NS_PER_SEC);
+ // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_FALSE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Start dump report and check output.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ ASSERT_EQ(1, report.value_metrics().data_size());
+
+ // {{}, ON}
+ ValueMetricData data = report.value_metrics().data(0);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+}
+
+/*
+ * Test slicing condition_true_nanos by state for metric that slices by state with a primary field,
+ * condition, and has multiple dimensions.
+ */
+TEST(ValueMetricProducerTest, TestSlicedStateWithMultipleDimensions) {
+ // Set up ValueMetricProducer.
+ ValueMetric metric =
+ ValueMetricProducerTestHelper::createMetricWithConditionAndState("UID_PROCESS_STATE");
+ metric.mutable_dimensions_in_what()->set_field(tagId);
+ metric.mutable_dimensions_in_what()->add_child()->set_field(1);
+ metric.mutable_dimensions_in_what()->add_child()->set_field(3);
+
+ MetricStateLink* stateLink = metric.add_state_link();
+ stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
+ auto fieldsInWhat = stateLink->mutable_fields_in_what();
+ *fieldsInWhat = CreateDimensions(tagId, {1 /* uid */});
+ auto fieldsInState = stateLink->mutable_fields_in_state();
+ *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
+
+ /*
+ bucket # 1 bucket # 2
+ 10 20 30 40 50 60 70 80 90 100 110 120 (seconds)
+ |------------------------------------------|---------------------------------|--
+
+ T F T (Condition)
+ (FOREGROUND)
+ x {1, 14}
+ |------|
+ 10
+
+ x {1, 16}
+ |------|
+ 10
+ x {2, 8}
+ |-------------|
+ 20
+
+ (BACKGROUND)
+ x {1, 14}
+ |-------------| |----------|---------------------------------|
+ 20 15 50
+
+ x {1, 16}
+ |-------------| |----------|---------------------------------|
+ 20 15 50
+
+ x {2, 8}
+ |----------| |----------|-------------------|
+ 15 15 30
+ */
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ // Uid 1 process state change from kStateUnknown -> Foreground
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC,
+ 1 /*uid*/, 3, 14 /*tag*/));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC,
+ 1 /*uid*/, 3, 16 /*tag*/));
+
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC,
+ 2 /*uid*/, 5, 8 /*tag*/));
+ return true;
+ }))
+ // Uid 1 process state change from Foreground -> Background
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+ 1 /*uid*/, 5, 14 /*tag*/));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+ 1 /*uid*/, 5, 16 /*tag*/));
+
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+ 2 /*uid*/, 7, 8 /*tag*/));
+
+ return true;
+ }))
+ // Uid 2 process state change from kStateUnknown -> Background
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 25 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
+ 2 /*uid*/, 9, 8 /*tag*/));
+
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
+ 1 /*uid*/, 9, 14 /* tag */));
+
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
+ 1 /*uid*/, 9, 16 /* tag */));
+
+ return true;
+ }))
+ // Condition changed to false.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+ 1 /*uid*/, 11, 14 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+ 1 /*uid*/, 11, 16 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+ 2 /*uid*/, 11, 8 /*tag*/));
+
+ return true;
+ }))
+ // Condition changed to true.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 45 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC,
+ 1 /*uid*/, 13, 14 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC,
+ 1 /*uid*/, 13, 16 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC,
+ 2 /*uid*/, 13, 8 /*tag*/));
+ return true;
+ }))
+ // Uid 2 process state change from Background -> Foreground
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 30 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 2 /*uid*/, 18, 8 /*tag*/));
+
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, 18, 14 /* tag */));
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, 18, 16 /* tag */));
+
+ return true;
+ }))
+ // Dump report pull.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 1 /*uid*/, 21, 14 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 1 /*uid*/, 21, 16 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 2 /*uid*/, 21, 8 /*tag*/));
+ return true;
+ }));
+
+ StateManager::getInstance().clear();
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithConditionAndState(
+ pullerManager, metric, {UID_PROCESS_STATE_ATOM_ID}, {}, ConditionState::kTrue);
+ EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
+
+ // Set up StateManager and check that StateTrackers are initialized.
+ StateManager::getInstance().registerListener(UID_PROCESS_STATE_ATOM_ID, valueProducer);
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
+
+ // Condition is true.
+ // Bucket status after uid 1 process state change kStateUnknown -> Foreground.
+ auto uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC, 1 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
+ StateManager::getInstance().onLogEvent(*uidProcessEvent);
+ ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension {uid 1, tag 16}.
+ auto it = valueProducer->mCurrentSlicedBucket.begin();
+ auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket status after uid 1 process state change Foreground -> Background.
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
+ StateManager::getInstance().onLogEvent(*uidProcessEvent);
+ ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(6UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension {uid 1, tag 16}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket status after uid 2 process state change kStateUnknown -> Background.
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucketStartTimeNs + 25 * NS_PER_SEC, 2 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
+ StateManager::getInstance().onLogEvent(*uidProcessEvent);
+ ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension {uid 2, tag 8}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 2, uid 8}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 25 * NS_PER_SEC);
+
+ // Value for key {{uid 2, uid 8}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Base for dimension {uid 1, tag 16}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket 1 status after condition change to false.
+ // All condition timers should be turned off.
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 40 * NS_PER_SEC);
+ ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension {uid 2, tag 8}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 2, uid 8}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 15 * NS_PER_SEC,
+ bucketStartTimeNs + 40 * NS_PER_SEC);
+
+ // Value for key {{uid 2, uid 8}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Base for dimension {uid 1, tag 16}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 40 * NS_PER_SEC);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 40 * NS_PER_SEC);
+
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket 1 status after condition change to true.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 45 * NS_PER_SEC);
+ ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension {uid 2, tag 8}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 2, uid 8}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 15 * NS_PER_SEC,
+ bucketStartTimeNs + 45 * NS_PER_SEC);
+
+ // Value for key {{uid 2, uid 8}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Base for dimension {uid 1, tag 16}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 45 * NS_PER_SEC);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 45 * NS_PER_SEC);
+
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Pull at end of first bucket.
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(
+ CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 14 /* tag */));
+ allData.push_back(
+ CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 16 /* tag */));
+ allData.push_back(
+ CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 2 /*uid*/, 13, 8 /*tag*/));
+ valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
+
+ // Buckets flushed after end of first bucket.
+ // All condition timers' behavior should rollover to bucket 2.
+ ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(5UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+ // Base for dimension {uid 2, tag 8}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 2, uid 8}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+ ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+ EXPECT_EQ(30 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+ // Value for key {{uid 2, uid 8}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Base for dimension {uid 1, tag 16}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+ ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+ EXPECT_EQ(35 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+ ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+ EXPECT_EQ(35 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+ ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+ EXPECT_EQ(10 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+ ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+ EXPECT_EQ(10 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket 2 status after uid 2 process state change Background->Foreground.
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 30 * NS_PER_SEC, 2 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
+ StateManager::getInstance().onLogEvent(*uidProcessEvent);
+
+ ASSERT_EQ(9UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+ // Base for dimension {uid 2, tag 8}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 2, uid 8}, FOREGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{uid 2, uid 8}, BACKGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 30 * NS_PER_SEC,
+ bucket2StartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{uid 2, uid 8}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Base for dimension {uid 1, tag 16}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Start dump report and check output.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ ASSERT_EQ(6, report.value_metrics().data_size());
+
+ // {{uid 1, tag 14}, FOREGROUND}.
+ auto data = report.value_metrics().data(0);
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+ // {{uid 1, tag 16}, BACKGROUND}.
+ data = report.value_metrics().data(1);
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+
+ // {{uid 1, tag 14}, BACKGROUND}.
+ data = report.value_metrics().data(2);
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+
+ // {{uid 1, tag 16}, FOREGROUND}.
+ data = report.value_metrics().data(3);
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+ // {{uid 2, tag 8}, FOREGROUND}.
+ data = report.value_metrics().data(4);
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+ // {{uid 2, tag 8}, BACKGROUND}.
+ data = report.value_metrics().data(5);
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
}
TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) {
@@ -4852,10 +6665,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithConditionAndState(
"BATTERY_SAVER_MODE_STATE");
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Condition changed to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
data->clear();
data->push_back(
@@ -4864,7 +6677,7 @@
}))
// Battery saver mode state changed to OFF.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC);
data->clear();
data->push_back(
@@ -4873,7 +6686,7 @@
}))
// Condition changed to false.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10 * NS_PER_SEC);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(
@@ -4907,23 +6720,31 @@
valueProducer->onConditionChanged(true, bucketStartTimeNs + 20 * NS_PER_SEC);
// Base for dimension key {}
ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
- std::unordered_map<HashableDimensionKey, std::vector<ValueMetricProducer::BaseInfo>>::iterator
+ std::unordered_map<HashableDimensionKey, ValueMetricProducer::DimensionsInWhatInfo>::iterator
itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY);
- EXPECT_TRUE(itBase->second[0].hasBase);
- EXPECT_EQ(3, itBase->second[0].base.long_value);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(BatterySaverModeStateChanged::ON,
- itBase->second[0].currentState.getValues()[0].mValue.int_value);
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ std::unordered_map<MetricDimensionKey, ValueMetricProducer::CurrentValueBucket>::iterator it =
+ valueProducer->mCurrentSlicedBucket.begin();
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
// Value for key {{}, -1}
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- std::unordered_map<MetricDimensionKey, std::vector<ValueMetricProducer::Interval>>::iterator
- it = valueProducer->mCurrentSlicedBucket.begin();
+ it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second[0].hasValue);
+ EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
// Bucket status after battery saver mode OFF event.
unique_ptr<LogEvent> batterySaverOffEvent =
@@ -4932,21 +6753,33 @@
// Base for dimension key {}
ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY);
- EXPECT_TRUE(itBase->second[0].hasBase);
- EXPECT_EQ(5, itBase->second[0].base.long_value);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(BatterySaverModeStateChanged::OFF,
- itBase->second[0].currentState.getValues()[0].mValue.int_value);
- // Value for key {{}, ON}
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, OFF}
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
it = valueProducer->mCurrentSlicedBucket.begin();
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::OFF,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 30 * NS_PER_SEC);
+ // Value for key {{}, ON}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(BatterySaverModeStateChanged::ON,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second[0].hasValue);
- EXPECT_EQ(2, it->second[0].value.long_value);
+ EXPECT_TRUE(it->second.intervals[0].hasValue);
+ EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 30 * NS_PER_SEC);
+ // Value for key {{}, -1}
+ it++;
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
// Pull at end of first bucket.
vector<shared_ptr<LogEvent>> allData;
@@ -4959,23 +6792,32 @@
// Base for dimension key {}
ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY);
- EXPECT_TRUE(itBase->second[0].hasBase);
- EXPECT_EQ(11, itBase->second[0].base.long_value);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(11, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(BatterySaverModeStateChanged::OFF,
- itBase->second[0].currentState.getValues()[0].mValue.int_value);
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, OFF}
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+ // Value for key {{}, ON}
+ it++;
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 30 * NS_PER_SEC);
+ // Value for key {{}, -1}
+ it++;
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
// Bucket 2 status after condition change to false.
valueProducer->onConditionChanged(false, bucket2StartTimeNs + 10 * NS_PER_SEC);
// Base for dimension key {}
ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY);
- EXPECT_FALSE(itBase->second[0].hasBase);
- EXPECT_TRUE(itBase->second[0].hasCurrentState);
- ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+ EXPECT_FALSE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(BatterySaverModeStateChanged::OFF,
- itBase->second[0].currentState.getValues()[0].mValue.int_value);
+ itBase->second.currentState.getValues()[0].mValue.int_value);
// Value for key {{}, OFF}
ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
it = valueProducer->mCurrentSlicedBucket.begin();
@@ -4983,8 +6825,21 @@
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(BatterySaverModeStateChanged::OFF,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second[0].hasValue);
- EXPECT_EQ(4, it->second[0].value.long_value);
+ EXPECT_TRUE(it->second.intervals[0].hasValue);
+ EXPECT_EQ(4, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucket2StartTimeNs + 10 * NS_PER_SEC);
+ // Value for key {{}, ON}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 30 * NS_PER_SEC);
+ // Value for key {{}, -1}
+ it++;
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
// Start dump report and check output.
ProtoOutputStream output;
@@ -5003,6 +6858,7 @@
EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(2, data.bucket_info(0).values(0).value_long());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
data = report.value_metrics().data(1);
EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
@@ -5011,6 +6867,8 @@
ASSERT_EQ(2, data.bucket_info_size());
EXPECT_EQ(6, data.bucket_info(0).values(0).value_long());
EXPECT_EQ(4, data.bucket_info(1).values(0).value_long());
+ EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
}
/*
diff --git a/bin/tests/metrics/metrics_test_helper.h b/bin/tests/metrics/metrics_test_helper.h
index eeb38a4..39232c1 100644
--- a/bin/tests/metrics/metrics_test_helper.h
+++ b/bin/tests/metrics/metrics_test_helper.h
@@ -38,11 +38,10 @@
int64_t nextPulltimeNs, int64_t intervalNs));
MOCK_METHOD3(UnRegisterReceiver,
void(int tagId, const ConfigKey& key, wp<PullDataReceiver> receiver));
- MOCK_METHOD5(Pull, bool(const int pullCode, const ConfigKey& key, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool useUids));
- MOCK_METHOD5(Pull,
- bool(const int pullCode, const vector<int32_t>& uids, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool useUids));
+ MOCK_METHOD4(Pull, bool(const int pullCode, const ConfigKey& key, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data));
+ MOCK_METHOD4(Pull, bool(const int pullCode, const vector<int32_t>& uids,
+ const int64_t eventTimeNs, vector<std::shared_ptr<LogEvent>>* data));
MOCK_METHOD2(RegisterPullUidProvider,
void(const ConfigKey& configKey, wp<PullUidProvider> provider));
MOCK_METHOD2(UnregisterPullUidProvider,
diff --git a/bin/tests/metrics/parsing_utils/config_update_utils_test.cpp b/bin/tests/metrics/parsing_utils/config_update_utils_test.cpp
new file mode 100644
index 0000000..d06f84f
--- /dev/null
+++ b/bin/tests/metrics/parsing_utils/config_update_utils_test.cpp
@@ -0,0 +1,3632 @@
+// Copyright (C) 2020 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 "src/metrics/parsing_utils/config_update_utils.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <private/android_filesystem_config.h>
+#include <stdio.h>
+
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+#include "packages/modules/StatsD/bin/src/statsd_config.pb.h"
+#include "src/condition/CombinationConditionTracker.h"
+#include "src/condition/SimpleConditionTracker.h"
+#include "src/matchers/CombinationAtomMatchingTracker.h"
+#include "src/metrics/DurationMetricProducer.h"
+#include "src/metrics/GaugeMetricProducer.h"
+#include "src/metrics/ValueMetricProducer.h"
+#include "src/metrics/parsing_utils/metrics_manager_util.h"
+#include "tests/statsd_test_util.h"
+
+using namespace testing;
+using android::sp;
+using android::os::statsd::Predicate;
+using std::map;
+using std::nullopt;
+using std::optional;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+namespace {
+
+ConfigKey key(123, 456);
+const int64_t timeBaseNs = 1000 * NS_PER_SEC;
+
+sp<UidMap> uidMap = new UidMap();
+sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+sp<AlarmMonitor> anomalyAlarmMonitor;
+sp<AlarmMonitor> periodicAlarmMonitor = new AlarmMonitor(
+ /*minDiffToUpdateRegisteredAlarmTimeSec=*/0,
+ [](const shared_ptr<IStatsCompanionService>&, int64_t) {},
+ [](const shared_ptr<IStatsCompanionService>&) {});
+set<int> allTagIds;
+vector<sp<AtomMatchingTracker>> oldAtomMatchingTrackers;
+unordered_map<int64_t, int> oldAtomMatchingTrackerMap;
+vector<sp<ConditionTracker>> oldConditionTrackers;
+unordered_map<int64_t, int> oldConditionTrackerMap;
+vector<sp<MetricProducer>> oldMetricProducers;
+unordered_map<int64_t, int> oldMetricProducerMap;
+std::vector<sp<AnomalyTracker>> oldAnomalyTrackers;
+unordered_map<int64_t, int> oldAlertTrackerMap;
+std::vector<sp<AlarmTracker>> oldAlarmTrackers;
+unordered_map<int, std::vector<int>> tmpConditionToMetricMap;
+unordered_map<int, std::vector<int>> tmpTrackerToMetricMap;
+unordered_map<int, std::vector<int>> tmpTrackerToConditionMap;
+unordered_map<int, std::vector<int>> tmpActivationAtomTrackerToMetricMap;
+unordered_map<int, std::vector<int>> tmpDeactivationAtomTrackerToMetricMap;
+vector<int> metricsWithActivation;
+map<int64_t, uint64_t> oldStateHashes;
+std::set<int64_t> noReportMetricIds;
+
+class ConfigUpdateTest : public ::testing::Test {
+public:
+ ConfigUpdateTest() {
+ }
+
+ void SetUp() override {
+ allTagIds.clear();
+ oldAtomMatchingTrackers.clear();
+ oldAtomMatchingTrackerMap.clear();
+ oldConditionTrackers.clear();
+ oldConditionTrackerMap.clear();
+ oldMetricProducers.clear();
+ oldMetricProducerMap.clear();
+ oldAnomalyTrackers.clear();
+ oldAlarmTrackers.clear();
+ tmpConditionToMetricMap.clear();
+ tmpTrackerToMetricMap.clear();
+ tmpTrackerToConditionMap.clear();
+ tmpActivationAtomTrackerToMetricMap.clear();
+ tmpDeactivationAtomTrackerToMetricMap.clear();
+ oldAlertTrackerMap.clear();
+ metricsWithActivation.clear();
+ oldStateHashes.clear();
+ noReportMetricIds.clear();
+ StateManager::getInstance().clear();
+ }
+};
+
+bool initConfig(const StatsdConfig& config) {
+ return initStatsdConfig(
+ key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+ timeBaseNs, timeBaseNs, allTagIds, oldAtomMatchingTrackers, oldAtomMatchingTrackerMap,
+ oldConditionTrackers, oldConditionTrackerMap, oldMetricProducers, oldMetricProducerMap,
+ oldAnomalyTrackers, oldAlarmTrackers, tmpConditionToMetricMap, tmpTrackerToMetricMap,
+ tmpTrackerToConditionMap, tmpActivationAtomTrackerToMetricMap,
+ tmpDeactivationAtomTrackerToMetricMap, oldAlertTrackerMap, metricsWithActivation,
+ oldStateHashes, noReportMetricIds);
+}
+
+EventMetric createEventMetric(string name, int64_t what, optional<int64_t> condition) {
+ EventMetric metric;
+ metric.set_id(StringToId(name));
+ metric.set_what(what);
+ if (condition) {
+ metric.set_condition(condition.value());
+ }
+ return metric;
+}
+
+CountMetric createCountMetric(string name, int64_t what, optional<int64_t> condition,
+ vector<int64_t> states) {
+ CountMetric metric;
+ metric.set_id(StringToId(name));
+ metric.set_what(what);
+ metric.set_bucket(TEN_MINUTES);
+ if (condition) {
+ metric.set_condition(condition.value());
+ }
+ for (const int64_t state : states) {
+ metric.add_slice_by_state(state);
+ }
+ return metric;
+}
+
+GaugeMetric createGaugeMetric(string name, int64_t what, GaugeMetric::SamplingType samplingType,
+ optional<int64_t> condition, optional<int64_t> triggerEvent) {
+ GaugeMetric metric;
+ metric.set_id(StringToId(name));
+ metric.set_what(what);
+ metric.set_bucket(TEN_MINUTES);
+ metric.set_sampling_type(samplingType);
+ if (condition) {
+ metric.set_condition(condition.value());
+ }
+ if (triggerEvent) {
+ metric.set_trigger_event(triggerEvent.value());
+ }
+ metric.mutable_gauge_fields_filter()->set_include_all(true);
+ return metric;
+}
+
+DurationMetric createDurationMetric(string name, int64_t what, optional<int64_t> condition,
+ vector<int64_t> states) {
+ DurationMetric metric;
+ metric.set_id(StringToId(name));
+ metric.set_what(what);
+ metric.set_bucket(TEN_MINUTES);
+ if (condition) {
+ metric.set_condition(condition.value());
+ }
+ for (const int64_t state : states) {
+ metric.add_slice_by_state(state);
+ }
+ return metric;
+}
+
+ValueMetric createValueMetric(string name, const AtomMatcher& what, optional<int64_t> condition,
+ vector<int64_t> states) {
+ ValueMetric metric;
+ metric.set_id(StringToId(name));
+ metric.set_what(what.id());
+ metric.set_bucket(TEN_MINUTES);
+ metric.mutable_value_field()->set_field(what.simple_atom_matcher().atom_id());
+ metric.mutable_value_field()->add_child()->set_field(2);
+ if (condition) {
+ metric.set_condition(condition.value());
+ }
+ for (const int64_t state : states) {
+ metric.add_slice_by_state(state);
+ }
+ return metric;
+}
+
+Alert createAlert(string name, int64_t metricId, int buckets, int64_t triggerSum) {
+ Alert alert;
+ alert.set_id(StringToId(name));
+ alert.set_metric_id(metricId);
+ alert.set_num_buckets(buckets);
+ alert.set_trigger_if_sum_gt(triggerSum);
+ return alert;
+}
+
+Subscription createSubscription(string name, Subscription_RuleType type, int64_t ruleId) {
+ Subscription subscription;
+ subscription.set_id(StringToId(name));
+ subscription.set_rule_type(type);
+ subscription.set_rule_id(ruleId);
+ subscription.mutable_broadcast_subscriber_details();
+ return subscription;
+}
+
+Alarm createAlarm(string name, int64_t offsetMillis, int64_t periodMillis) {
+ Alarm alarm;
+ alarm.set_id(StringToId(name));
+ alarm.set_offset_millis(offsetMillis);
+ alarm.set_period_millis(periodMillis);
+ return alarm;
+}
+} // anonymous namespace
+
+TEST_F(ConfigUpdateTest, TestSimpleMatcherPreserve) {
+ StatsdConfig config;
+ AtomMatcher matcher = CreateSimpleAtomMatcher("TEST", /*atom=*/10);
+ int64_t matcherId = matcher.id();
+ *config.add_atom_matcher() = matcher;
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ vector<UpdateStatus> matchersToUpdate(1, UPDATE_UNKNOWN);
+ vector<bool> cycleTracker(1, false);
+ unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+ newAtomMatchingTrackerMap[matcherId] = 0;
+ EXPECT_TRUE(determineMatcherUpdateStatus(config, 0, oldAtomMatchingTrackerMap,
+ oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
+ matchersToUpdate, cycleTracker));
+ EXPECT_EQ(matchersToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestSimpleMatcherReplace) {
+ StatsdConfig config;
+ AtomMatcher matcher = CreateSimpleAtomMatcher("TEST", /*atom=*/10);
+ *config.add_atom_matcher() = matcher;
+
+ EXPECT_TRUE(initConfig(config));
+
+ StatsdConfig newConfig;
+ // Same id, different atom, so should be replaced.
+ AtomMatcher newMatcher = CreateSimpleAtomMatcher("TEST", /*atom=*/11);
+ int64_t matcherId = newMatcher.id();
+ EXPECT_EQ(matcherId, matcher.id());
+ *newConfig.add_atom_matcher() = newMatcher;
+
+ vector<UpdateStatus> matchersToUpdate(1, UPDATE_UNKNOWN);
+ vector<bool> cycleTracker(1, false);
+ unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+ newAtomMatchingTrackerMap[matcherId] = 0;
+ EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 0, oldAtomMatchingTrackerMap,
+ oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
+ matchersToUpdate, cycleTracker));
+ EXPECT_EQ(matchersToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestSimpleMatcherNew) {
+ StatsdConfig config;
+ AtomMatcher matcher = CreateSimpleAtomMatcher("TEST", /*atom=*/10);
+ *config.add_atom_matcher() = matcher;
+
+ EXPECT_TRUE(initConfig(config));
+
+ StatsdConfig newConfig;
+ // Different id, so should be a new matcher.
+ AtomMatcher newMatcher = CreateSimpleAtomMatcher("DIFFERENT_NAME", /*atom=*/10);
+ int64_t matcherId = newMatcher.id();
+ EXPECT_NE(matcherId, matcher.id());
+ *newConfig.add_atom_matcher() = newMatcher;
+
+ vector<UpdateStatus> matchersToUpdate(1, UPDATE_UNKNOWN);
+ vector<bool> cycleTracker(1, false);
+ unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+ newAtomMatchingTrackerMap[matcherId] = 0;
+ EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 0, oldAtomMatchingTrackerMap,
+ oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
+ matchersToUpdate, cycleTracker));
+ EXPECT_EQ(matchersToUpdate[0], UPDATE_NEW);
+}
+
+TEST_F(ConfigUpdateTest, TestCombinationMatcherPreserve) {
+ StatsdConfig config;
+ AtomMatcher matcher1 = CreateSimpleAtomMatcher("TEST1", /*atom=*/10);
+ int64_t matcher1Id = matcher1.id();
+ *config.add_atom_matcher() = matcher1;
+
+ AtomMatcher matcher2 = CreateSimpleAtomMatcher("TEST2", /*atom=*/11);
+ *config.add_atom_matcher() = matcher2;
+ int64_t matcher2Id = matcher2.id();
+
+ AtomMatcher matcher3;
+ matcher3.set_id(StringToId("TEST3"));
+ AtomMatcher_Combination* combination = matcher3.mutable_combination();
+ combination->set_operation(LogicalOperation::OR);
+ combination->add_matcher(matcher1Id);
+ combination->add_matcher(matcher2Id);
+ int64_t matcher3Id = matcher3.id();
+ *config.add_atom_matcher() = matcher3;
+
+ EXPECT_TRUE(initConfig(config));
+
+ StatsdConfig newConfig;
+ unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+ // Same matchers, different order, all should be preserved.
+ *newConfig.add_atom_matcher() = matcher2;
+ newAtomMatchingTrackerMap[matcher2Id] = 0;
+ *newConfig.add_atom_matcher() = matcher3;
+ newAtomMatchingTrackerMap[matcher3Id] = 1;
+ *newConfig.add_atom_matcher() = matcher1;
+ newAtomMatchingTrackerMap[matcher1Id] = 2;
+
+ vector<UpdateStatus> matchersToUpdate(3, UPDATE_UNKNOWN);
+ vector<bool> cycleTracker(3, false);
+ // Only update the combination. It should recurse the two child matchers and preserve all 3.
+ EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 1, oldAtomMatchingTrackerMap,
+ oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
+ matchersToUpdate, cycleTracker));
+ EXPECT_EQ(matchersToUpdate[0], UPDATE_PRESERVE);
+ EXPECT_EQ(matchersToUpdate[1], UPDATE_PRESERVE);
+ EXPECT_EQ(matchersToUpdate[2], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestCombinationMatcherReplace) {
+ StatsdConfig config;
+ AtomMatcher matcher1 = CreateSimpleAtomMatcher("TEST1", /*atom=*/10);
+ int64_t matcher1Id = matcher1.id();
+ *config.add_atom_matcher() = matcher1;
+
+ AtomMatcher matcher2 = CreateSimpleAtomMatcher("TEST2", /*atom=*/11);
+ *config.add_atom_matcher() = matcher2;
+ int64_t matcher2Id = matcher2.id();
+
+ AtomMatcher matcher3;
+ matcher3.set_id(StringToId("TEST3"));
+ AtomMatcher_Combination* combination = matcher3.mutable_combination();
+ combination->set_operation(LogicalOperation::OR);
+ combination->add_matcher(matcher1Id);
+ combination->add_matcher(matcher2Id);
+ int64_t matcher3Id = matcher3.id();
+ *config.add_atom_matcher() = matcher3;
+
+ EXPECT_TRUE(initConfig(config));
+
+ // Change the logical operation of the combination matcher, causing a replacement.
+ matcher3.mutable_combination()->set_operation(LogicalOperation::AND);
+
+ StatsdConfig newConfig;
+ unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+ *newConfig.add_atom_matcher() = matcher2;
+ newAtomMatchingTrackerMap[matcher2Id] = 0;
+ *newConfig.add_atom_matcher() = matcher3;
+ newAtomMatchingTrackerMap[matcher3Id] = 1;
+ *newConfig.add_atom_matcher() = matcher1;
+ newAtomMatchingTrackerMap[matcher1Id] = 2;
+
+ vector<UpdateStatus> matchersToUpdate(3, UPDATE_UNKNOWN);
+ vector<bool> cycleTracker(3, false);
+ // Only update the combination. The simple matchers should not be evaluated.
+ EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 1, oldAtomMatchingTrackerMap,
+ oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
+ matchersToUpdate, cycleTracker));
+ EXPECT_EQ(matchersToUpdate[0], UPDATE_UNKNOWN);
+ EXPECT_EQ(matchersToUpdate[1], UPDATE_REPLACE);
+ EXPECT_EQ(matchersToUpdate[2], UPDATE_UNKNOWN);
+}
+
+TEST_F(ConfigUpdateTest, TestCombinationMatcherDepsChange) {
+ StatsdConfig config;
+ AtomMatcher matcher1 = CreateSimpleAtomMatcher("TEST1", /*atom=*/10);
+ int64_t matcher1Id = matcher1.id();
+ *config.add_atom_matcher() = matcher1;
+
+ AtomMatcher matcher2 = CreateSimpleAtomMatcher("TEST2", /*atom=*/11);
+ *config.add_atom_matcher() = matcher2;
+ int64_t matcher2Id = matcher2.id();
+
+ AtomMatcher matcher3;
+ matcher3.set_id(StringToId("TEST3"));
+ AtomMatcher_Combination* combination = matcher3.mutable_combination();
+ combination->set_operation(LogicalOperation::OR);
+ combination->add_matcher(matcher1Id);
+ combination->add_matcher(matcher2Id);
+ int64_t matcher3Id = matcher3.id();
+ *config.add_atom_matcher() = matcher3;
+
+ EXPECT_TRUE(initConfig(config));
+
+ // Change a dependency of matcher 3.
+ matcher2.mutable_simple_atom_matcher()->set_atom_id(12);
+
+ StatsdConfig newConfig;
+ unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+ *newConfig.add_atom_matcher() = matcher2;
+ newAtomMatchingTrackerMap[matcher2Id] = 0;
+ *newConfig.add_atom_matcher() = matcher3;
+ newAtomMatchingTrackerMap[matcher3Id] = 1;
+ *newConfig.add_atom_matcher() = matcher1;
+ newAtomMatchingTrackerMap[matcher1Id] = 2;
+
+ vector<UpdateStatus> matchersToUpdate(3, UPDATE_UNKNOWN);
+ vector<bool> cycleTracker(3, false);
+ // Only update the combination.
+ EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 1, oldAtomMatchingTrackerMap,
+ oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
+ matchersToUpdate, cycleTracker));
+ // Matcher 2 and matcher3 must be reevaluated. Matcher 1 might, but does not need to be.
+ EXPECT_EQ(matchersToUpdate[0], UPDATE_REPLACE);
+ EXPECT_EQ(matchersToUpdate[1], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateMatchers) {
+ StatsdConfig config;
+ // Will be preserved.
+ AtomMatcher simple1 = CreateSimpleAtomMatcher("SIMPLE1", /*atom=*/10);
+ int64_t simple1Id = simple1.id();
+ *config.add_atom_matcher() = simple1;
+
+ // Will be replaced.
+ AtomMatcher simple2 = CreateSimpleAtomMatcher("SIMPLE2", /*atom=*/11);
+ *config.add_atom_matcher() = simple2;
+ int64_t simple2Id = simple2.id();
+
+ // Will be removed.
+ AtomMatcher simple3 = CreateSimpleAtomMatcher("SIMPLE3", /*atom=*/12);
+ *config.add_atom_matcher() = simple3;
+ int64_t simple3Id = simple3.id();
+
+ // Will be preserved.
+ AtomMatcher combination1;
+ combination1.set_id(StringToId("combination1"));
+ AtomMatcher_Combination* combination = combination1.mutable_combination();
+ combination->set_operation(LogicalOperation::NOT);
+ combination->add_matcher(simple1Id);
+ int64_t combination1Id = combination1.id();
+ *config.add_atom_matcher() = combination1;
+
+ // Will be replaced since it depends on simple2.
+ AtomMatcher combination2;
+ combination2.set_id(StringToId("combination2"));
+ combination = combination2.mutable_combination();
+ combination->set_operation(LogicalOperation::AND);
+ combination->add_matcher(simple1Id);
+ combination->add_matcher(simple2Id);
+ int64_t combination2Id = combination2.id();
+ *config.add_atom_matcher() = combination2;
+
+ EXPECT_TRUE(initConfig(config));
+
+ // Change simple2, causing simple2 and combination2 to be replaced.
+ simple2.mutable_simple_atom_matcher()->set_atom_id(111);
+
+ // 2 new matchers: simple4 and combination3:
+ AtomMatcher simple4 = CreateSimpleAtomMatcher("SIMPLE4", /*atom=*/13);
+ int64_t simple4Id = simple4.id();
+
+ AtomMatcher combination3;
+ combination3.set_id(StringToId("combination3"));
+ combination = combination3.mutable_combination();
+ combination->set_operation(LogicalOperation::AND);
+ combination->add_matcher(simple4Id);
+ combination->add_matcher(simple2Id);
+ int64_t combination3Id = combination3.id();
+
+ StatsdConfig newConfig;
+ *newConfig.add_atom_matcher() = combination3;
+ *newConfig.add_atom_matcher() = simple2;
+ *newConfig.add_atom_matcher() = combination2;
+ *newConfig.add_atom_matcher() = simple1;
+ *newConfig.add_atom_matcher() = simple4;
+ *newConfig.add_atom_matcher() = combination1;
+
+ set<int> newTagIds;
+ unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+ vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers;
+ set<int64_t> replacedMatchers;
+ EXPECT_TRUE(updateAtomMatchingTrackers(
+ newConfig, uidMap, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers, newTagIds,
+ newAtomMatchingTrackerMap, newAtomMatchingTrackers, replacedMatchers));
+
+ ASSERT_EQ(newTagIds.size(), 3);
+ EXPECT_EQ(newTagIds.count(10), 1);
+ EXPECT_EQ(newTagIds.count(111), 1);
+ EXPECT_EQ(newTagIds.count(13), 1);
+
+ ASSERT_EQ(newAtomMatchingTrackerMap.size(), 6);
+ EXPECT_EQ(newAtomMatchingTrackerMap.at(combination3Id), 0);
+ EXPECT_EQ(newAtomMatchingTrackerMap.at(simple2Id), 1);
+ EXPECT_EQ(newAtomMatchingTrackerMap.at(combination2Id), 2);
+ EXPECT_EQ(newAtomMatchingTrackerMap.at(simple1Id), 3);
+ EXPECT_EQ(newAtomMatchingTrackerMap.at(simple4Id), 4);
+ EXPECT_EQ(newAtomMatchingTrackerMap.at(combination1Id), 5);
+
+ ASSERT_EQ(newAtomMatchingTrackers.size(), 6);
+ // Make sure all atom matchers are initialized:
+ for (const sp<AtomMatchingTracker>& tracker : newAtomMatchingTrackers) {
+ EXPECT_TRUE(tracker->mInitialized);
+ }
+ // Make sure preserved atom matchers are the same.
+ EXPECT_EQ(oldAtomMatchingTrackers[oldAtomMatchingTrackerMap.at(simple1Id)],
+ newAtomMatchingTrackers[newAtomMatchingTrackerMap.at(simple1Id)]);
+ EXPECT_EQ(oldAtomMatchingTrackers[oldAtomMatchingTrackerMap.at(combination1Id)],
+ newAtomMatchingTrackers[newAtomMatchingTrackerMap.at(combination1Id)]);
+ // Make sure replaced matchers are different.
+ EXPECT_NE(oldAtomMatchingTrackers[oldAtomMatchingTrackerMap.at(simple2Id)],
+ newAtomMatchingTrackers[newAtomMatchingTrackerMap.at(simple2Id)]);
+ EXPECT_NE(oldAtomMatchingTrackers[oldAtomMatchingTrackerMap.at(combination2Id)],
+ newAtomMatchingTrackers[newAtomMatchingTrackerMap.at(combination2Id)]);
+
+ // Validation, make sure the matchers have the proper ids/indices. Could do more checks here.
+ EXPECT_EQ(newAtomMatchingTrackers[0]->getId(), combination3Id);
+ EXPECT_EQ(newAtomMatchingTrackers[0]->mIndex, 0);
+ EXPECT_EQ(newAtomMatchingTrackers[1]->getId(), simple2Id);
+ EXPECT_EQ(newAtomMatchingTrackers[1]->mIndex, 1);
+ EXPECT_EQ(newAtomMatchingTrackers[2]->getId(), combination2Id);
+ EXPECT_EQ(newAtomMatchingTrackers[2]->mIndex, 2);
+ EXPECT_EQ(newAtomMatchingTrackers[3]->getId(), simple1Id);
+ EXPECT_EQ(newAtomMatchingTrackers[3]->mIndex, 3);
+ EXPECT_EQ(newAtomMatchingTrackers[4]->getId(), simple4Id);
+ EXPECT_EQ(newAtomMatchingTrackers[4]->mIndex, 4);
+ EXPECT_EQ(newAtomMatchingTrackers[5]->getId(), combination1Id);
+ EXPECT_EQ(newAtomMatchingTrackers[5]->mIndex, 5);
+
+ // Verify child indices of Combination Matchers are correct.
+ CombinationAtomMatchingTracker* combinationTracker1 =
+ static_cast<CombinationAtomMatchingTracker*>(newAtomMatchingTrackers[5].get());
+ vector<int>* childMatchers = &combinationTracker1->mChildren;
+ EXPECT_EQ(childMatchers->size(), 1);
+ EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 3), childMatchers->end());
+
+ CombinationAtomMatchingTracker* combinationTracker2 =
+ static_cast<CombinationAtomMatchingTracker*>(newAtomMatchingTrackers[2].get());
+ childMatchers = &combinationTracker2->mChildren;
+ EXPECT_EQ(childMatchers->size(), 2);
+ EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 1), childMatchers->end());
+ EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 3), childMatchers->end());
+
+ CombinationAtomMatchingTracker* combinationTracker3 =
+ static_cast<CombinationAtomMatchingTracker*>(newAtomMatchingTrackers[0].get());
+ childMatchers = &combinationTracker3->mChildren;
+ EXPECT_EQ(childMatchers->size(), 2);
+ EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 1), childMatchers->end());
+ EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 4), childMatchers->end());
+
+ // Expect replacedMatchers to have simple2 and combination2
+ ASSERT_EQ(replacedMatchers.size(), 2);
+ EXPECT_NE(replacedMatchers.find(simple2Id), replacedMatchers.end());
+ EXPECT_NE(replacedMatchers.find(combination2Id), replacedMatchers.end());
+}
+
+TEST_F(ConfigUpdateTest, TestSimpleConditionPreserve) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ set<int64_t> replacedMatchers;
+ vector<UpdateStatus> conditionsToUpdate(1, UPDATE_UNKNOWN);
+ vector<bool> cycleTracker(1, false);
+ unordered_map<int64_t, int> newConditionTrackerMap;
+ newConditionTrackerMap[predicate.id()] = 0;
+ EXPECT_TRUE(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap,
+ oldConditionTrackers, newConditionTrackerMap,
+ replacedMatchers, conditionsToUpdate, cycleTracker));
+ EXPECT_EQ(conditionsToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestSimpleConditionReplace) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ EXPECT_TRUE(initConfig(config));
+
+ // Modify the predicate.
+ config.mutable_predicate(0)->mutable_simple_predicate()->set_count_nesting(true);
+
+ set<int64_t> replacedMatchers;
+ vector<UpdateStatus> conditionsToUpdate(1, UPDATE_UNKNOWN);
+ vector<bool> cycleTracker(1, false);
+ unordered_map<int64_t, int> newConditionTrackerMap;
+ newConditionTrackerMap[predicate.id()] = 0;
+ EXPECT_TRUE(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap,
+ oldConditionTrackers, newConditionTrackerMap,
+ replacedMatchers, conditionsToUpdate, cycleTracker));
+ EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestSimpleConditionDepsChange) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ int64_t startMatcherId = startMatcher.id();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ EXPECT_TRUE(initConfig(config));
+
+ // Start matcher was replaced.
+ set<int64_t> replacedMatchers;
+ replacedMatchers.insert(startMatcherId);
+
+ vector<UpdateStatus> conditionsToUpdate(1, UPDATE_UNKNOWN);
+ vector<bool> cycleTracker(1, false);
+ unordered_map<int64_t, int> newConditionTrackerMap;
+ newConditionTrackerMap[predicate.id()] = 0;
+ EXPECT_TRUE(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap,
+ oldConditionTrackers, newConditionTrackerMap,
+ replacedMatchers, conditionsToUpdate, cycleTracker));
+ EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestCombinationConditionPreserve) {
+ StatsdConfig config;
+ AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = screenOnMatcher;
+ AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = screenOffMatcher;
+
+ Predicate simple1 = CreateScreenIsOnPredicate();
+ *config.add_predicate() = simple1;
+ Predicate simple2 = CreateScreenIsOffPredicate();
+ *config.add_predicate() = simple2;
+
+ Predicate combination1;
+ combination1.set_id(StringToId("COMBINATION1"));
+ Predicate_Combination* combinationInternal = combination1.mutable_combination();
+ combinationInternal->set_operation(LogicalOperation::NAND);
+ combinationInternal->add_predicate(simple1.id());
+ combinationInternal->add_predicate(simple2.id());
+ *config.add_predicate() = combination1;
+
+ EXPECT_TRUE(initConfig(config));
+
+ // Same predicates, different order
+ StatsdConfig newConfig;
+ unordered_map<int64_t, int> newConditionTrackerMap;
+ *newConfig.add_predicate() = combination1;
+ newConditionTrackerMap[combination1.id()] = 0;
+ *newConfig.add_predicate() = simple2;
+ newConditionTrackerMap[simple2.id()] = 1;
+ *newConfig.add_predicate() = simple1;
+ newConditionTrackerMap[simple1.id()] = 2;
+
+ set<int64_t> replacedMatchers;
+ vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN);
+ vector<bool> cycleTracker(3, false);
+ // Only update the combination. It should recurse the two child predicates and preserve all 3.
+ EXPECT_TRUE(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap,
+ oldConditionTrackers, newConditionTrackerMap,
+ replacedMatchers, conditionsToUpdate, cycleTracker));
+ EXPECT_EQ(conditionsToUpdate[0], UPDATE_PRESERVE);
+ EXPECT_EQ(conditionsToUpdate[1], UPDATE_PRESERVE);
+ EXPECT_EQ(conditionsToUpdate[2], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestCombinationConditionReplace) {
+ StatsdConfig config;
+ AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = screenOnMatcher;
+ AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = screenOffMatcher;
+
+ Predicate simple1 = CreateScreenIsOnPredicate();
+ *config.add_predicate() = simple1;
+ Predicate simple2 = CreateScreenIsOffPredicate();
+ *config.add_predicate() = simple2;
+
+ Predicate combination1;
+ combination1.set_id(StringToId("COMBINATION1"));
+ Predicate_Combination* combinationInternal = combination1.mutable_combination();
+ combinationInternal->set_operation(LogicalOperation::NAND);
+ combinationInternal->add_predicate(simple1.id());
+ combinationInternal->add_predicate(simple2.id());
+ *config.add_predicate() = combination1;
+
+ EXPECT_TRUE(initConfig(config));
+
+ // Changing the logical operation changes the predicate definition, so it should be replaced.
+ combination1.mutable_combination()->set_operation(LogicalOperation::OR);
+
+ StatsdConfig newConfig;
+ unordered_map<int64_t, int> newConditionTrackerMap;
+ *newConfig.add_predicate() = combination1;
+ newConditionTrackerMap[combination1.id()] = 0;
+ *newConfig.add_predicate() = simple2;
+ newConditionTrackerMap[simple2.id()] = 1;
+ *newConfig.add_predicate() = simple1;
+ newConditionTrackerMap[simple1.id()] = 2;
+
+ set<int64_t> replacedMatchers;
+ vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN);
+ vector<bool> cycleTracker(3, false);
+ // Only update the combination. The simple conditions should not be evaluated.
+ EXPECT_TRUE(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap,
+ oldConditionTrackers, newConditionTrackerMap,
+ replacedMatchers, conditionsToUpdate, cycleTracker));
+ EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE);
+ EXPECT_EQ(conditionsToUpdate[1], UPDATE_UNKNOWN);
+ EXPECT_EQ(conditionsToUpdate[2], UPDATE_UNKNOWN);
+}
+
+TEST_F(ConfigUpdateTest, TestCombinationConditionDepsChange) {
+ StatsdConfig config;
+ AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = screenOnMatcher;
+ AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = screenOffMatcher;
+
+ Predicate simple1 = CreateScreenIsOnPredicate();
+ *config.add_predicate() = simple1;
+ Predicate simple2 = CreateScreenIsOffPredicate();
+ *config.add_predicate() = simple2;
+
+ Predicate combination1;
+ combination1.set_id(StringToId("COMBINATION1"));
+ Predicate_Combination* combinationInternal = combination1.mutable_combination();
+ combinationInternal->set_operation(LogicalOperation::NAND);
+ combinationInternal->add_predicate(simple1.id());
+ combinationInternal->add_predicate(simple2.id());
+ *config.add_predicate() = combination1;
+
+ EXPECT_TRUE(initConfig(config));
+
+ simple2.mutable_simple_predicate()->set_count_nesting(false);
+
+ StatsdConfig newConfig;
+ unordered_map<int64_t, int> newConditionTrackerMap;
+ *newConfig.add_predicate() = combination1;
+ newConditionTrackerMap[combination1.id()] = 0;
+ *newConfig.add_predicate() = simple2;
+ newConditionTrackerMap[simple2.id()] = 1;
+ *newConfig.add_predicate() = simple1;
+ newConditionTrackerMap[simple1.id()] = 2;
+
+ set<int64_t> replacedMatchers;
+ vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN);
+ vector<bool> cycleTracker(3, false);
+ // Only update the combination. Simple2 and combination1 must be evaluated.
+ EXPECT_TRUE(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap,
+ oldConditionTrackers, newConditionTrackerMap,
+ replacedMatchers, conditionsToUpdate, cycleTracker));
+ EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE);
+ EXPECT_EQ(conditionsToUpdate[1], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateConditions) {
+ StatsdConfig config;
+ // Add atom matchers. These are mostly needed for initStatsdConfig
+ AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+ int64_t matcher1Id = matcher1.id();
+ *config.add_atom_matcher() = matcher1;
+
+ AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+ int64_t matcher2Id = matcher2.id();
+ *config.add_atom_matcher() = matcher2;
+
+ AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
+ int64_t matcher3Id = matcher3.id();
+ *config.add_atom_matcher() = matcher3;
+
+ AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher();
+ int64_t matcher4Id = matcher4.id();
+ *config.add_atom_matcher() = matcher4;
+
+ AtomMatcher matcher5 = CreateBatterySaverModeStartAtomMatcher();
+ int64_t matcher5Id = matcher5.id();
+ *config.add_atom_matcher() = matcher5;
+
+ AtomMatcher matcher6 = CreateBatterySaverModeStopAtomMatcher();
+ int64_t matcher6Id = matcher6.id();
+ *config.add_atom_matcher() = matcher6;
+
+ // Add the predicates.
+ // Will be preserved.
+ Predicate simple1 = CreateScreenIsOnPredicate();
+ int64_t simple1Id = simple1.id();
+ *config.add_predicate() = simple1;
+
+ // Will be preserved.
+ Predicate simple2 = CreateScheduledJobPredicate();
+ int64_t simple2Id = simple2.id();
+ *config.add_predicate() = simple2;
+
+ // Will be replaced.
+ Predicate simple3 = CreateBatterySaverModePredicate();
+ int64_t simple3Id = simple3.id();
+ *config.add_predicate() = simple3;
+
+ // Will be preserved
+ Predicate combination1;
+ combination1.set_id(StringToId("COMBINATION1"));
+ combination1.mutable_combination()->set_operation(LogicalOperation::AND);
+ combination1.mutable_combination()->add_predicate(simple1Id);
+ combination1.mutable_combination()->add_predicate(simple2Id);
+ int64_t combination1Id = combination1.id();
+ *config.add_predicate() = combination1;
+
+ // Will be replaced since simple3 will be replaced.
+ Predicate combination2;
+ combination2.set_id(StringToId("COMBINATION2"));
+ combination2.mutable_combination()->set_operation(LogicalOperation::OR);
+ combination2.mutable_combination()->add_predicate(simple1Id);
+ combination2.mutable_combination()->add_predicate(simple3Id);
+ int64_t combination2Id = combination2.id();
+ *config.add_predicate() = combination2;
+
+ // Will be removed.
+ Predicate combination3;
+ combination3.set_id(StringToId("COMBINATION3"));
+ combination3.mutable_combination()->set_operation(LogicalOperation::NOT);
+ combination3.mutable_combination()->add_predicate(simple2Id);
+ int64_t combination3Id = combination3.id();
+ *config.add_predicate() = combination3;
+
+ EXPECT_TRUE(initConfig(config));
+
+ // Mark marcher 5 as replaced. Causes simple3, and therefore combination2 to be replaced.
+ set<int64_t> replacedMatchers;
+ replacedMatchers.insert(matcher6Id);
+
+ // Change the condition of simple1 to false.
+ ASSERT_EQ(oldConditionTrackers[0]->getConditionId(), simple1Id);
+ LogEvent event(/*uid=*/0, /*pid=*/0); // Empty event is fine since there are no dimensions.
+ // Mark the stop matcher as matched, condition should be false.
+ vector<MatchingState> eventMatcherValues(6, MatchingState::kNotMatched);
+ eventMatcherValues[1] = MatchingState::kMatched;
+ vector<ConditionState> tmpConditionCache(6, ConditionState::kNotEvaluated);
+ vector<bool> conditionChangeCache(6, false);
+ oldConditionTrackers[0]->evaluateCondition(event, eventMatcherValues, oldConditionTrackers,
+ tmpConditionCache, conditionChangeCache);
+ EXPECT_EQ(tmpConditionCache[0], ConditionState::kFalse);
+ EXPECT_EQ(conditionChangeCache[0], true);
+
+ // New combination predicate. Should have an initial condition of true since it is NOT(simple1).
+ Predicate combination4;
+ combination4.set_id(StringToId("COMBINATION4"));
+ combination4.mutable_combination()->set_operation(LogicalOperation::NOT);
+ combination4.mutable_combination()->add_predicate(simple1Id);
+ int64_t combination4Id = combination4.id();
+ *config.add_predicate() = combination4;
+
+ // Map the matchers in reverse order to force the indices to change.
+ std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+ const int matcher6Index = 0;
+ newAtomMatchingTrackerMap[matcher6Id] = 0;
+ const int matcher5Index = 1;
+ newAtomMatchingTrackerMap[matcher5Id] = 1;
+ const int matcher4Index = 2;
+ newAtomMatchingTrackerMap[matcher4Id] = 2;
+ const int matcher3Index = 3;
+ newAtomMatchingTrackerMap[matcher3Id] = 3;
+ const int matcher2Index = 4;
+ newAtomMatchingTrackerMap[matcher2Id] = 4;
+ const int matcher1Index = 5;
+ newAtomMatchingTrackerMap[matcher1Id] = 5;
+
+ StatsdConfig newConfig;
+ *newConfig.add_predicate() = simple3;
+ const int simple3Index = 0;
+ *newConfig.add_predicate() = combination2;
+ const int combination2Index = 1;
+ *newConfig.add_predicate() = combination4;
+ const int combination4Index = 2;
+ *newConfig.add_predicate() = simple2;
+ const int simple2Index = 3;
+ *newConfig.add_predicate() = combination1;
+ const int combination1Index = 4;
+ *newConfig.add_predicate() = simple1;
+ const int simple1Index = 5;
+
+ unordered_map<int64_t, int> newConditionTrackerMap;
+ vector<sp<ConditionTracker>> newConditionTrackers;
+ unordered_map<int, vector<int>> trackerToConditionMap;
+ std::vector<ConditionState> conditionCache;
+ std::set<int64_t> replacedConditions;
+ EXPECT_TRUE(updateConditions(key, newConfig, newAtomMatchingTrackerMap, replacedMatchers,
+ oldConditionTrackerMap, oldConditionTrackers,
+ newConditionTrackerMap, newConditionTrackers,
+ trackerToConditionMap, conditionCache, replacedConditions));
+
+ unordered_map<int64_t, int> expectedConditionTrackerMap = {
+ {simple1Id, simple1Index}, {simple2Id, simple2Index},
+ {simple3Id, simple3Index}, {combination1Id, combination1Index},
+ {combination2Id, combination2Index}, {combination4Id, combination4Index},
+ };
+ EXPECT_THAT(newConditionTrackerMap, ContainerEq(expectedConditionTrackerMap));
+
+ ASSERT_EQ(newConditionTrackers.size(), 6);
+ // Make sure all conditions are initialized:
+ for (const sp<ConditionTracker>& tracker : newConditionTrackers) {
+ EXPECT_TRUE(tracker->mInitialized);
+ }
+
+ // Make sure preserved conditions are the same.
+ EXPECT_EQ(oldConditionTrackers[oldConditionTrackerMap.at(simple1Id)],
+ newConditionTrackers[newConditionTrackerMap.at(simple1Id)]);
+ EXPECT_EQ(oldConditionTrackers[oldConditionTrackerMap.at(simple2Id)],
+ newConditionTrackers[newConditionTrackerMap.at(simple2Id)]);
+ EXPECT_EQ(oldConditionTrackers[oldConditionTrackerMap.at(combination1Id)],
+ newConditionTrackers[newConditionTrackerMap.at(combination1Id)]);
+
+ // Make sure replaced conditions are different and included in replacedConditions.
+ EXPECT_NE(oldConditionTrackers[oldConditionTrackerMap.at(simple3Id)],
+ newConditionTrackers[newConditionTrackerMap.at(simple3Id)]);
+ EXPECT_NE(oldConditionTrackers[oldConditionTrackerMap.at(combination2Id)],
+ newConditionTrackers[newConditionTrackerMap.at(combination2Id)]);
+ EXPECT_THAT(replacedConditions, ContainerEq(set({simple3Id, combination2Id})));
+
+ // Verify the trackerToConditionMap
+ ASSERT_EQ(trackerToConditionMap.size(), 6);
+ const vector<int>& matcher1Conditions = trackerToConditionMap[matcher1Index];
+ EXPECT_THAT(matcher1Conditions, UnorderedElementsAre(simple1Index, combination1Index,
+ combination2Index, combination4Index));
+ const vector<int>& matcher2Conditions = trackerToConditionMap[matcher2Index];
+ EXPECT_THAT(matcher2Conditions, UnorderedElementsAre(simple1Index, combination1Index,
+ combination2Index, combination4Index));
+ const vector<int>& matcher3Conditions = trackerToConditionMap[matcher3Index];
+ EXPECT_THAT(matcher3Conditions, UnorderedElementsAre(simple2Index, combination1Index));
+ const vector<int>& matcher4Conditions = trackerToConditionMap[matcher4Index];
+ EXPECT_THAT(matcher4Conditions, UnorderedElementsAre(simple2Index, combination1Index));
+ const vector<int>& matcher5Conditions = trackerToConditionMap[matcher5Index];
+ EXPECT_THAT(matcher5Conditions, UnorderedElementsAre(simple3Index, combination2Index));
+ const vector<int>& matcher6Conditions = trackerToConditionMap[matcher6Index];
+ EXPECT_THAT(matcher6Conditions, UnorderedElementsAre(simple3Index, combination2Index));
+
+ // Verify the conditionCache. Specifically, simple1 is false and combination4 is true.
+ ASSERT_EQ(conditionCache.size(), 6);
+ EXPECT_EQ(conditionCache[simple1Index], ConditionState::kFalse);
+ EXPECT_EQ(conditionCache[simple2Index], ConditionState::kUnknown);
+ EXPECT_EQ(conditionCache[simple3Index], ConditionState::kUnknown);
+ EXPECT_EQ(conditionCache[combination1Index], ConditionState::kUnknown);
+ EXPECT_EQ(conditionCache[combination2Index], ConditionState::kUnknown);
+ EXPECT_EQ(conditionCache[combination4Index], ConditionState::kTrue);
+
+ // Verify tracker indices/ids are correct.
+ EXPECT_EQ(newConditionTrackers[simple1Index]->getConditionId(), simple1Id);
+ EXPECT_EQ(newConditionTrackers[simple1Index]->mIndex, simple1Index);
+ EXPECT_TRUE(newConditionTrackers[simple1Index]->IsSimpleCondition());
+ EXPECT_EQ(newConditionTrackers[simple2Index]->getConditionId(), simple2Id);
+ EXPECT_EQ(newConditionTrackers[simple2Index]->mIndex, simple2Index);
+ EXPECT_TRUE(newConditionTrackers[simple2Index]->IsSimpleCondition());
+ EXPECT_EQ(newConditionTrackers[simple3Index]->getConditionId(), simple3Id);
+ EXPECT_EQ(newConditionTrackers[simple3Index]->mIndex, simple3Index);
+ EXPECT_TRUE(newConditionTrackers[simple3Index]->IsSimpleCondition());
+ EXPECT_EQ(newConditionTrackers[combination1Index]->getConditionId(), combination1Id);
+ EXPECT_EQ(newConditionTrackers[combination1Index]->mIndex, combination1Index);
+ EXPECT_FALSE(newConditionTrackers[combination1Index]->IsSimpleCondition());
+ EXPECT_EQ(newConditionTrackers[combination2Index]->getConditionId(), combination2Id);
+ EXPECT_EQ(newConditionTrackers[combination2Index]->mIndex, combination2Index);
+ EXPECT_FALSE(newConditionTrackers[combination2Index]->IsSimpleCondition());
+ EXPECT_EQ(newConditionTrackers[combination4Index]->getConditionId(), combination4Id);
+ EXPECT_EQ(newConditionTrackers[combination4Index]->mIndex, combination4Index);
+ EXPECT_FALSE(newConditionTrackers[combination4Index]->IsSimpleCondition());
+
+ // Verify preserved trackers have indices updated.
+ SimpleConditionTracker* simpleTracker1 =
+ static_cast<SimpleConditionTracker*>(newConditionTrackers[simple1Index].get());
+ EXPECT_EQ(simpleTracker1->mStartLogMatcherIndex, matcher1Index);
+ EXPECT_EQ(simpleTracker1->mStopLogMatcherIndex, matcher2Index);
+ EXPECT_EQ(simpleTracker1->mStopAllLogMatcherIndex, -1);
+
+ SimpleConditionTracker* simpleTracker2 =
+ static_cast<SimpleConditionTracker*>(newConditionTrackers[simple2Index].get());
+ EXPECT_EQ(simpleTracker2->mStartLogMatcherIndex, matcher3Index);
+ EXPECT_EQ(simpleTracker2->mStopLogMatcherIndex, matcher4Index);
+ EXPECT_EQ(simpleTracker2->mStopAllLogMatcherIndex, -1);
+
+ CombinationConditionTracker* combinationTracker1 = static_cast<CombinationConditionTracker*>(
+ newConditionTrackers[combination1Index].get());
+ EXPECT_THAT(combinationTracker1->mChildren, UnorderedElementsAre(simple1Index, simple2Index));
+ EXPECT_THAT(combinationTracker1->mUnSlicedChildren,
+ UnorderedElementsAre(simple1Index, simple2Index));
+ EXPECT_THAT(combinationTracker1->mSlicedChildren, IsEmpty());
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateStates) {
+ StatsdConfig config;
+ // Add states.
+ // Will be replaced because we add a state map.
+ State state1 = CreateScreenState();
+ int64_t state1Id = state1.id();
+ *config.add_state() = state1;
+
+ // Will be preserved.
+ State state2 = CreateUidProcessState();
+ int64_t state2Id = state2.id();
+ *config.add_state() = state2;
+
+ // Will be replaced since the atom changes from overlay to screen.
+ State state3 = CreateOverlayState();
+ int64_t state3Id = state3.id();
+ *config.add_state() = state3;
+
+ EXPECT_TRUE(initConfig(config));
+
+ // Change definitions of state1 and state3.
+ int64_t screenOnId = 0x4321, screenOffId = 0x1234;
+ *state1.mutable_map() = CreateScreenStateSimpleOnOffMap(screenOnId, screenOffId);
+ state3.set_atom_id(util::SCREEN_STATE_CHANGED);
+
+ StatsdConfig newConfig;
+ *newConfig.add_state() = state3;
+ *newConfig.add_state() = state1;
+ *newConfig.add_state() = state2;
+
+ unordered_map<int64_t, int> stateAtomIdMap;
+ unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
+ map<int64_t, uint64_t> newStateProtoHashes;
+ set<int64_t> replacedStates;
+ EXPECT_TRUE(updateStates(newConfig, oldStateHashes, stateAtomIdMap, allStateGroupMaps,
+ newStateProtoHashes, replacedStates));
+ EXPECT_THAT(replacedStates, ContainerEq(set({state1Id, state3Id})));
+
+ unordered_map<int64_t, int> expectedStateAtomIdMap = {
+ {state1Id, util::SCREEN_STATE_CHANGED},
+ {state2Id, util::UID_PROCESS_STATE_CHANGED},
+ {state3Id, util::SCREEN_STATE_CHANGED}};
+ EXPECT_THAT(stateAtomIdMap, ContainerEq(expectedStateAtomIdMap));
+
+ unordered_map<int64_t, unordered_map<int, int64_t>> expectedStateGroupMaps = {
+ {state1Id,
+ {{android::view::DisplayStateEnum::DISPLAY_STATE_OFF, screenOffId},
+ {android::view::DisplayStateEnum::DISPLAY_STATE_ON, screenOnId}}}};
+ EXPECT_THAT(allStateGroupMaps, ContainerEq(expectedStateGroupMaps));
+}
+
+TEST_F(ConfigUpdateTest, TestEventMetricPreserve) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ EventMetric* metric = config.add_event_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestEventMetricActivationAdded) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ EventMetric* metric = config.add_event_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ // Add a metric activation, which should change the proto, causing replacement.
+ MetricActivation* activation = config.add_metric_activation();
+ activation->set_metric_id(12345);
+ EventActivation* eventActivation = activation->add_event_activation();
+ eventActivation->set_atom_matcher_id(startMatcher.id());
+ eventActivation->set_ttl_seconds(5);
+
+ unordered_map<int64_t, int> metricToActivationMap = {{12345, 0}};
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestEventMetricWhatChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ EventMetric* metric = config.add_event_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestEventMetricConditionChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ EventMetric* metric = config.add_event_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestMetricConditionLinkDepsChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ Predicate linkPredicate = CreateScreenIsOffPredicate();
+ *config.add_predicate() = linkPredicate;
+
+ EventMetric* metric = config.add_event_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+ // Doesn't make sense as a real metric definition, but suffices as a separate predicate
+ // From the one in the condition.
+ MetricConditionLink* link = metric->add_links();
+ link->set_condition(linkPredicate.id());
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{linkPredicate.id()},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestEventMetricActivationDepsChange) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ EventMetric* metric = config.add_event_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+
+ MetricActivation* activation = config.add_metric_activation();
+ activation->set_metric_id(12345);
+ EventActivation* eventActivation = activation->add_event_activation();
+ eventActivation->set_atom_matcher_id(startMatcher.id());
+ eventActivation->set_ttl_seconds(5);
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap = {{12345, 0}};
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {startMatcher.id()}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestCountMetricPreserve) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+ State sliceState = CreateScreenState();
+ *config.add_state() = sliceState;
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+ metric->add_slice_by_state(sliceState.id());
+ metric->set_bucket(ONE_HOUR);
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestCountMetricDefinitionChange) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+ metric->set_bucket(ONE_HOUR);
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ // Change bucket size, which should change the proto, causing replacement.
+ metric->set_bucket(TEN_MINUTES);
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestCountMetricWhatChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+ metric->set_bucket(ONE_HOUR);
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestCountMetricConditionChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+ metric->set_bucket(ONE_HOUR);
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestCountMetricStateChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ State sliceState = CreateScreenState();
+ *config.add_state() = sliceState;
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->add_slice_by_state(sliceState.id());
+ metric->set_bucket(ONE_HOUR);
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{sliceState.id()}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestGaugeMetricPreserve) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ *config.add_gauge_metric() = createGaugeMetric(
+ "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, predicate.id(), nullopt);
+
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestGaugeMetricDefinitionChange) {
+ StatsdConfig config;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ *config.add_gauge_metric() = createGaugeMetric(
+ "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, nullopt);
+
+ EXPECT_TRUE(initConfig(config));
+
+ // Change split bucket on app upgrade, which should change the proto, causing replacement.
+ config.mutable_gauge_metric(0)->set_split_bucket_for_app_upgrade(false);
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestGaugeMetricWhatChanged) {
+ StatsdConfig config;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ *config.add_gauge_metric() = createGaugeMetric(
+ "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, nullopt);
+
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestGaugeMetricConditionChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ *config.add_gauge_metric() = createGaugeMetric(
+ "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, predicate.id(), nullopt);
+
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestGaugeMetricTriggerEventChanged) {
+ StatsdConfig config;
+ AtomMatcher triggerEvent = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = triggerEvent;
+ AtomMatcher whatMatcher = CreateTemperatureAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ *config.add_gauge_metric() = createGaugeMetric(
+ "GAUGE1", whatMatcher.id(), GaugeMetric::FIRST_N_SAMPLES, nullopt, triggerEvent.id());
+
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {triggerEvent.id()}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestDurationMetricPreserve) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+
+ Predicate what = CreateScreenIsOnPredicate();
+ *config.add_predicate() = what;
+ Predicate condition = CreateScreenIsOffPredicate();
+ *config.add_predicate() = condition;
+
+ State sliceState = CreateScreenState();
+ *config.add_state() = sliceState;
+
+ *config.add_duration_metric() =
+ createDurationMetric("DURATION1", what.id(), condition.id(), {sliceState.id()});
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestDurationMetricDefinitionChange) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+
+ Predicate what = CreateScreenIsOnPredicate();
+ *config.add_predicate() = what;
+
+ *config.add_duration_metric() = createDurationMetric("DURATION1", what.id(), nullopt, {});
+ EXPECT_TRUE(initConfig(config));
+
+ config.mutable_duration_metric(0)->set_aggregation_type(DurationMetric::MAX_SPARSE);
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap, /*replacedMatchers*/ {},
+ /*replacedConditions=*/{}, /*replacedStates=*/{},
+ metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestDurationMetricWhatChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+
+ Predicate what = CreateScreenIsOnPredicate();
+ *config.add_predicate() = what;
+
+ *config.add_duration_metric() = createDurationMetric("DURATION1", what.id(), nullopt, {});
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{what.id()},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestDurationMetricConditionChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+
+ Predicate what = CreateScreenIsOnPredicate();
+ *config.add_predicate() = what;
+ Predicate condition = CreateScreenIsOffPredicate();
+ *config.add_predicate() = condition;
+
+ *config.add_duration_metric() = createDurationMetric("DURATION", what.id(), condition.id(), {});
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{condition.id()},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestDurationMetricStateChange) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+
+ Predicate what = CreateScreenIsOnPredicate();
+ *config.add_predicate() = what;
+
+ State sliceState = CreateScreenState();
+ *config.add_state() = sliceState;
+
+ *config.add_duration_metric() =
+ createDurationMetric("DURATION1", what.id(), nullopt, {sliceState.id()});
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{sliceState.id()}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestValueMetricPreserve) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateTemperatureAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+ State sliceState = CreateScreenState();
+ *config.add_state() = sliceState;
+
+ *config.add_value_metric() =
+ createValueMetric("VALUE1", whatMatcher, predicate.id(), {sliceState.id()});
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestValueMetricDefinitionChange) {
+ StatsdConfig config;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ *config.add_value_metric() = createValueMetric("VALUE1", whatMatcher, nullopt, {});
+ EXPECT_TRUE(initConfig(config));
+
+ // Change skip zero diff output, which should change the proto, causing replacement.
+ config.mutable_value_metric(0)->set_skip_zero_diff_output(true);
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestValueMetricWhatChanged) {
+ StatsdConfig config;
+ AtomMatcher whatMatcher = CreateTemperatureAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ *config.add_value_metric() = createValueMetric("VALUE1", whatMatcher, nullopt, {});
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestValueMetricConditionChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateTemperatureAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ *config.add_value_metric() = createValueMetric("VALUE1", whatMatcher, predicate.id(), {});
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestValueMetricStateChanged) {
+ StatsdConfig config;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ State sliceState = CreateScreenState();
+ *config.add_state() = sliceState;
+
+ *config.add_value_metric() =
+ createValueMetric("VALUE1", whatMatcher, nullopt, {sliceState.id()});
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{sliceState.id()}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateEventMetrics) {
+ StatsdConfig config;
+
+ // Add atom matchers/predicates. These are mostly needed for initStatsdConfig
+ AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+ int64_t matcher1Id = matcher1.id();
+ *config.add_atom_matcher() = matcher1;
+
+ AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+ int64_t matcher2Id = matcher2.id();
+ *config.add_atom_matcher() = matcher2;
+
+ AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
+ int64_t matcher3Id = matcher3.id();
+ *config.add_atom_matcher() = matcher3;
+
+ AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher();
+ int64_t matcher4Id = matcher4.id();
+ *config.add_atom_matcher() = matcher4;
+
+ AtomMatcher matcher5 = CreateBatterySaverModeStartAtomMatcher();
+ int64_t matcher5Id = matcher5.id();
+ *config.add_atom_matcher() = matcher5;
+
+ Predicate predicate1 = CreateScreenIsOnPredicate();
+ int64_t predicate1Id = predicate1.id();
+ *config.add_predicate() = predicate1;
+
+ Predicate predicate2 = CreateScheduledJobPredicate();
+ int64_t predicate2Id = predicate2.id();
+ *config.add_predicate() = predicate2;
+
+ // Add a few event metrics.
+ // Will be preserved.
+ EventMetric event1 = createEventMetric("EVENT1", matcher1Id, predicate2Id);
+ int64_t event1Id = event1.id();
+ *config.add_event_metric() = event1;
+
+ // Will be replaced.
+ EventMetric event2 = createEventMetric("EVENT2", matcher2Id, nullopt);
+ int64_t event2Id = event2.id();
+ *config.add_event_metric() = event2;
+
+ // Will be replaced.
+ EventMetric event3 = createEventMetric("EVENT3", matcher3Id, nullopt);
+ int64_t event3Id = event3.id();
+ *config.add_event_metric() = event3;
+
+ MetricActivation event3Activation;
+ event3Activation.set_metric_id(event3Id);
+ EventActivation* eventActivation = event3Activation.add_event_activation();
+ eventActivation->set_atom_matcher_id(matcher5Id);
+ eventActivation->set_ttl_seconds(5);
+ *config.add_metric_activation() = event3Activation;
+
+ // Will be replaced.
+ EventMetric event4 = createEventMetric("EVENT4", matcher4Id, predicate1Id);
+ int64_t event4Id = event4.id();
+ *config.add_event_metric() = event4;
+
+ // Will be deleted.
+ EventMetric event5 = createEventMetric("EVENT5", matcher5Id, nullopt);
+ int64_t event5Id = event5.id();
+ *config.add_event_metric() = event5;
+
+ EXPECT_TRUE(initConfig(config));
+
+ // Used later to ensure the condition wizard is replaced. Get it before doing the update.
+ sp<ConditionWizard> oldConditionWizard = oldMetricProducers[0]->mWizard;
+ EXPECT_EQ(oldConditionWizard->getStrongCount(), oldMetricProducers.size() + 1);
+
+ // Add a condition to event2, causing it to be replaced.
+ event2.set_condition(predicate1Id);
+
+ // Mark matcher 5 as replaced. Causes event3 to be replaced.
+ set<int64_t> replacedMatchers;
+ replacedMatchers.insert(matcher5Id);
+
+ // Mark predicate 1 as replaced. Causes event4 to be replaced.
+ set<int64_t> replacedConditions;
+ replacedConditions.insert(predicate1Id);
+
+ // Fake that predicate 2 is true.
+ ASSERT_EQ(oldMetricProducers[0]->getMetricId(), event1Id);
+ oldMetricProducers[0]->onConditionChanged(true, /*timestamp=*/0);
+ EXPECT_EQ(oldMetricProducers[0]->mCondition, ConditionState::kTrue);
+
+ // New event metric. Should have an initial condition of true since it depends on predicate2.
+ EventMetric event6 = createEventMetric("EVENT6", matcher3Id, predicate2Id);
+ int64_t event6Id = event6.id();
+ MetricActivation event6Activation;
+ event6Activation.set_metric_id(event6Id);
+ eventActivation = event6Activation.add_event_activation();
+ eventActivation->set_atom_matcher_id(matcher5Id);
+ eventActivation->set_ttl_seconds(20);
+
+ // Map the matchers and predicates in reverse order to force the indices to change.
+ std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+ const int matcher5Index = 0;
+ newAtomMatchingTrackerMap[matcher5Id] = 0;
+ const int matcher4Index = 1;
+ newAtomMatchingTrackerMap[matcher4Id] = 1;
+ const int matcher3Index = 2;
+ newAtomMatchingTrackerMap[matcher3Id] = 2;
+ const int matcher2Index = 3;
+ newAtomMatchingTrackerMap[matcher2Id] = 3;
+ const int matcher1Index = 4;
+ newAtomMatchingTrackerMap[matcher1Id] = 4;
+ // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+ vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(5);
+ std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
+ newAtomMatchingTrackers.begin());
+
+ std::unordered_map<int64_t, int> newConditionTrackerMap;
+ const int predicate2Index = 0;
+ newConditionTrackerMap[predicate2Id] = 0;
+ const int predicate1Index = 1;
+ newConditionTrackerMap[predicate1Id] = 1;
+ // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them.
+ vector<sp<ConditionTracker>> newConditionTrackers(2);
+ std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
+ newConditionTrackers.begin());
+ // Fake that predicate2 is true.
+ vector<ConditionState> conditionCache = {ConditionState::kTrue, ConditionState::kUnknown};
+
+ StatsdConfig newConfig;
+ *newConfig.add_event_metric() = event6;
+ const int event6Index = 0;
+ *newConfig.add_event_metric() = event3;
+ const int event3Index = 1;
+ *newConfig.add_event_metric() = event1;
+ const int event1Index = 2;
+ *newConfig.add_event_metric() = event4;
+ const int event4Index = 3;
+ *newConfig.add_event_metric() = event2;
+ const int event2Index = 4;
+ *newConfig.add_metric_activation() = event3Activation;
+ *newConfig.add_metric_activation() = event6Activation;
+
+ // Output data structures to validate.
+ unordered_map<int64_t, int> newMetricProducerMap;
+ vector<sp<MetricProducer>> newMetricProducers;
+ unordered_map<int, vector<int>> conditionToMetricMap;
+ unordered_map<int, vector<int>> trackerToMetricMap;
+ set<int64_t> noReportMetricIds;
+ unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+ vector<int> metricsWithActivation;
+ set<int64_t> replacedMetrics;
+ EXPECT_TRUE(updateMetrics(
+ key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
+ newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions,
+ newConditionTrackers, conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{},
+ /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
+ newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, replacedMetrics));
+
+ unordered_map<int64_t, int> expectedMetricProducerMap = {
+ {event1Id, event1Index}, {event2Id, event2Index}, {event3Id, event3Index},
+ {event4Id, event4Index}, {event6Id, event6Index},
+ };
+ EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+ EXPECT_EQ(replacedMetrics, set<int64_t>({event2Id, event3Id, event4Id}));
+
+ // Make sure preserved metrics are the same.
+ ASSERT_EQ(newMetricProducers.size(), 5);
+ EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(event1Id)],
+ newMetricProducers[newMetricProducerMap.at(event1Id)]);
+
+ // Make sure replaced metrics are different.
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(event2Id)],
+ newMetricProducers[newMetricProducerMap.at(event2Id)]);
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(event3Id)],
+ newMetricProducers[newMetricProducerMap.at(event3Id)]);
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(event4Id)],
+ newMetricProducers[newMetricProducerMap.at(event4Id)]);
+
+ // Verify the conditionToMetricMap.
+ ASSERT_EQ(conditionToMetricMap.size(), 2);
+ const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
+ EXPECT_THAT(condition1Metrics, UnorderedElementsAre(event2Index, event4Index));
+ const vector<int>& condition2Metrics = conditionToMetricMap[predicate2Index];
+ EXPECT_THAT(condition2Metrics, UnorderedElementsAre(event1Index, event6Index));
+
+ // Verify the trackerToMetricMap.
+ ASSERT_EQ(trackerToMetricMap.size(), 4);
+ const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
+ EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(event1Index));
+ const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index];
+ EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(event2Index));
+ const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
+ EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(event3Index, event6Index));
+ const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index];
+ EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(event4Index));
+
+ // Verify event activation/deactivation maps.
+ ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 1);
+ EXPECT_THAT(activationAtomTrackerToMetricMap[matcher5Index],
+ UnorderedElementsAre(event3Index, event6Index));
+ ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
+ ASSERT_EQ(metricsWithActivation.size(), 2);
+ EXPECT_THAT(metricsWithActivation, UnorderedElementsAre(event3Index, event6Index));
+
+ // Verify tracker indices/ids/conditions are correct.
+ EXPECT_EQ(newMetricProducers[event1Index]->getMetricId(), event1Id);
+ EXPECT_EQ(newMetricProducers[event1Index]->mConditionTrackerIndex, predicate2Index);
+ EXPECT_EQ(newMetricProducers[event1Index]->mCondition, ConditionState::kTrue);
+ EXPECT_EQ(newMetricProducers[event2Index]->getMetricId(), event2Id);
+ EXPECT_EQ(newMetricProducers[event2Index]->mConditionTrackerIndex, predicate1Index);
+ EXPECT_EQ(newMetricProducers[event2Index]->mCondition, ConditionState::kUnknown);
+ EXPECT_EQ(newMetricProducers[event3Index]->getMetricId(), event3Id);
+ EXPECT_EQ(newMetricProducers[event3Index]->mConditionTrackerIndex, -1);
+ EXPECT_EQ(newMetricProducers[event3Index]->mCondition, ConditionState::kTrue);
+ EXPECT_EQ(newMetricProducers[event4Index]->getMetricId(), event4Id);
+ EXPECT_EQ(newMetricProducers[event4Index]->mConditionTrackerIndex, predicate1Index);
+ EXPECT_EQ(newMetricProducers[event4Index]->mCondition, ConditionState::kUnknown);
+ EXPECT_EQ(newMetricProducers[event6Index]->getMetricId(), event6Id);
+ EXPECT_EQ(newMetricProducers[event6Index]->mConditionTrackerIndex, predicate2Index);
+ EXPECT_EQ(newMetricProducers[event6Index]->mCondition, ConditionState::kTrue);
+
+ sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard;
+ EXPECT_NE(newConditionWizard, oldConditionWizard);
+ EXPECT_EQ(newConditionWizard->getStrongCount(), newMetricProducers.size() + 1);
+ oldMetricProducers.clear();
+ // Only reference to the old wizard should be the one in the test.
+ EXPECT_EQ(oldConditionWizard->getStrongCount(), 1);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateCountMetrics) {
+ StatsdConfig config;
+
+ // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig.
+ AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+ int64_t matcher1Id = matcher1.id();
+ *config.add_atom_matcher() = matcher1;
+
+ AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+ int64_t matcher2Id = matcher2.id();
+ *config.add_atom_matcher() = matcher2;
+
+ AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
+ int64_t matcher3Id = matcher3.id();
+ *config.add_atom_matcher() = matcher3;
+
+ AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher();
+ int64_t matcher4Id = matcher4.id();
+ *config.add_atom_matcher() = matcher4;
+
+ AtomMatcher matcher5 = CreateBatterySaverModeStartAtomMatcher();
+ int64_t matcher5Id = matcher5.id();
+ *config.add_atom_matcher() = matcher5;
+
+ Predicate predicate1 = CreateScreenIsOnPredicate();
+ int64_t predicate1Id = predicate1.id();
+ *config.add_predicate() = predicate1;
+
+ State state1 = CreateScreenStateWithOnOffMap(0x123, 0x321);
+ int64_t state1Id = state1.id();
+ *config.add_state() = state1;
+
+ State state2 = CreateScreenState();
+ int64_t state2Id = state2.id();
+ *config.add_state() = state2;
+
+ // Add a few count metrics.
+ // Will be preserved.
+ CountMetric count1 = createCountMetric("COUNT1", matcher1Id, predicate1Id, {state1Id});
+ int64_t count1Id = count1.id();
+ *config.add_count_metric() = count1;
+
+ // Will be replaced.
+ CountMetric count2 = createCountMetric("COUNT2", matcher2Id, nullopt, {});
+ int64_t count2Id = count2.id();
+ *config.add_count_metric() = count2;
+
+ // Will be replaced.
+ CountMetric count3 = createCountMetric("COUNT3", matcher3Id, nullopt, {});
+ int64_t count3Id = count3.id();
+ *config.add_count_metric() = count3;
+
+ // Will be replaced.
+ CountMetric count4 = createCountMetric("COUNT4", matcher4Id, nullopt, {state2Id});
+ int64_t count4Id = count4.id();
+ *config.add_count_metric() = count4;
+
+ // Will be deleted.
+ CountMetric count5 = createCountMetric("COUNT5", matcher5Id, nullopt, {});
+ int64_t count5Id = count5.id();
+ *config.add_count_metric() = count5;
+
+ EXPECT_TRUE(initConfig(config));
+
+ // Change bucket size of count2, causing it to be replaced.
+ count2.set_bucket(ONE_HOUR);
+
+ // Mark matcher 3 as replaced. Causes count3 to be replaced.
+ set<int64_t> replacedMatchers;
+ replacedMatchers.insert(matcher3Id);
+
+ // Mark state 2 as replaced and change the state to be about a different atom.
+ // Causes count4 to be replaced.
+ set<int64_t> replacedStates;
+ replacedStates.insert(state2Id);
+ state2.set_atom_id(util::BATTERY_SAVER_MODE_STATE_CHANGED);
+
+ // Fake that predicate 1 is true for count metric 1.
+ ASSERT_EQ(oldMetricProducers[0]->getMetricId(), count1Id);
+ oldMetricProducers[0]->onConditionChanged(true, /*timestamp=*/0);
+ EXPECT_EQ(oldMetricProducers[0]->mCondition, ConditionState::kTrue);
+
+ EXPECT_EQ(StateManager::getInstance().getStateTrackersCount(), 1);
+ // Tell the StateManager that the screen is on.
+ unique_ptr<LogEvent> event =
+ CreateScreenStateChangedEvent(0, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+ StateManager::getInstance().onLogEvent(*event);
+
+ // New count metric. Should have an initial condition of true since it depends on predicate1.
+ CountMetric count6 = createCountMetric("EVENT6", matcher2Id, predicate1Id, {state1Id});
+ int64_t count6Id = count6.id();
+
+ // Map the matchers and predicates in reverse order to force the indices to change.
+ std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+ const int matcher5Index = 0;
+ newAtomMatchingTrackerMap[matcher5Id] = 0;
+ const int matcher4Index = 1;
+ newAtomMatchingTrackerMap[matcher4Id] = 1;
+ const int matcher3Index = 2;
+ newAtomMatchingTrackerMap[matcher3Id] = 2;
+ const int matcher2Index = 3;
+ newAtomMatchingTrackerMap[matcher2Id] = 3;
+ const int matcher1Index = 4;
+ newAtomMatchingTrackerMap[matcher1Id] = 4;
+ // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+ vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(5);
+ std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
+ newAtomMatchingTrackers.begin());
+
+ std::unordered_map<int64_t, int> newConditionTrackerMap;
+ const int predicate1Index = 0;
+ newConditionTrackerMap[predicate1Id] = 0;
+ // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them.
+ vector<sp<ConditionTracker>> newConditionTrackers(1);
+ std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
+ newConditionTrackers.begin());
+ // Fake that predicate1 is true for all new metrics.
+ vector<ConditionState> conditionCache = {ConditionState::kTrue};
+
+ StatsdConfig newConfig;
+ *newConfig.add_count_metric() = count6;
+ const int count6Index = 0;
+ *newConfig.add_count_metric() = count3;
+ const int count3Index = 1;
+ *newConfig.add_count_metric() = count1;
+ const int count1Index = 2;
+ *newConfig.add_count_metric() = count4;
+ const int count4Index = 3;
+ *newConfig.add_count_metric() = count2;
+ const int count2Index = 4;
+
+ *newConfig.add_state() = state1;
+ *newConfig.add_state() = state2;
+
+ unordered_map<int64_t, int> stateAtomIdMap;
+ unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
+ map<int64_t, uint64_t> stateProtoHashes;
+ EXPECT_TRUE(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes));
+ EXPECT_EQ(stateAtomIdMap[state2Id], util::BATTERY_SAVER_MODE_STATE_CHANGED);
+
+ // Output data structures to validate.
+ unordered_map<int64_t, int> newMetricProducerMap;
+ vector<sp<MetricProducer>> newMetricProducers;
+ unordered_map<int, vector<int>> conditionToMetricMap;
+ unordered_map<int, vector<int>> trackerToMetricMap;
+ set<int64_t> noReportMetricIds;
+ unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+ vector<int> metricsWithActivation;
+ set<int64_t> replacedMetrics;
+ EXPECT_TRUE(updateMetrics(
+ key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
+ newAtomMatchingTrackers, newConditionTrackerMap, /*replacedConditions=*/{},
+ newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates,
+ oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, newMetricProducers,
+ conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, replacedMetrics));
+
+ unordered_map<int64_t, int> expectedMetricProducerMap = {
+ {count1Id, count1Index}, {count2Id, count2Index}, {count3Id, count3Index},
+ {count4Id, count4Index}, {count6Id, count6Index},
+ };
+ EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+ EXPECT_EQ(replacedMetrics, set<int64_t>({count2Id, count3Id, count4Id}));
+
+ // Make sure preserved metrics are the same.
+ ASSERT_EQ(newMetricProducers.size(), 5);
+ EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(count1Id)],
+ newMetricProducers[newMetricProducerMap.at(count1Id)]);
+
+ // Make sure replaced metrics are different.
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(count2Id)],
+ newMetricProducers[newMetricProducerMap.at(count2Id)]);
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(count3Id)],
+ newMetricProducers[newMetricProducerMap.at(count3Id)]);
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(count4Id)],
+ newMetricProducers[newMetricProducerMap.at(count4Id)]);
+
+ // Verify the conditionToMetricMap.
+ ASSERT_EQ(conditionToMetricMap.size(), 1);
+ const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
+ EXPECT_THAT(condition1Metrics, UnorderedElementsAre(count1Index, count6Index));
+
+ // Verify the trackerToMetricMap.
+ ASSERT_EQ(trackerToMetricMap.size(), 4);
+ const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
+ EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(count1Index));
+ const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index];
+ EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(count2Index, count6Index));
+ const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
+ EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(count3Index));
+ const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index];
+ EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(count4Index));
+
+ // Verify event activation/deactivation maps.
+ ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
+ ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
+ ASSERT_EQ(metricsWithActivation.size(), 0);
+
+ // Verify tracker indices/ids/conditions/states are correct.
+ EXPECT_EQ(newMetricProducers[count1Index]->getMetricId(), count1Id);
+ EXPECT_EQ(newMetricProducers[count1Index]->mConditionTrackerIndex, predicate1Index);
+ EXPECT_EQ(newMetricProducers[count1Index]->mCondition, ConditionState::kTrue);
+ EXPECT_THAT(newMetricProducers[count1Index]->getSlicedStateAtoms(),
+ UnorderedElementsAre(util::SCREEN_STATE_CHANGED));
+ EXPECT_EQ(newMetricProducers[count2Index]->getMetricId(), count2Id);
+ EXPECT_EQ(newMetricProducers[count2Index]->mConditionTrackerIndex, -1);
+ EXPECT_EQ(newMetricProducers[count2Index]->mCondition, ConditionState::kTrue);
+ EXPECT_TRUE(newMetricProducers[count2Index]->getSlicedStateAtoms().empty());
+ EXPECT_EQ(newMetricProducers[count3Index]->getMetricId(), count3Id);
+ EXPECT_EQ(newMetricProducers[count3Index]->mConditionTrackerIndex, -1);
+ EXPECT_EQ(newMetricProducers[count3Index]->mCondition, ConditionState::kTrue);
+ EXPECT_TRUE(newMetricProducers[count3Index]->getSlicedStateAtoms().empty());
+ EXPECT_EQ(newMetricProducers[count4Index]->getMetricId(), count4Id);
+ EXPECT_EQ(newMetricProducers[count4Index]->mConditionTrackerIndex, -1);
+ EXPECT_EQ(newMetricProducers[count4Index]->mCondition, ConditionState::kTrue);
+ EXPECT_THAT(newMetricProducers[count4Index]->getSlicedStateAtoms(),
+ UnorderedElementsAre(util::BATTERY_SAVER_MODE_STATE_CHANGED));
+ EXPECT_EQ(newMetricProducers[count6Index]->getMetricId(), count6Id);
+ EXPECT_EQ(newMetricProducers[count6Index]->mConditionTrackerIndex, predicate1Index);
+ EXPECT_EQ(newMetricProducers[count6Index]->mCondition, ConditionState::kTrue);
+ EXPECT_THAT(newMetricProducers[count6Index]->getSlicedStateAtoms(),
+ UnorderedElementsAre(util::SCREEN_STATE_CHANGED));
+
+ oldMetricProducers.clear();
+ // Ensure that the screen state StateTracker did not get deleted and replaced.
+ EXPECT_EQ(StateManager::getInstance().getStateTrackersCount(), 2);
+ FieldValue screenState;
+ StateManager::getInstance().getStateValue(util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY,
+ &screenState);
+ EXPECT_EQ(screenState.mValue.int_value, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateGaugeMetrics) {
+ StatsdConfig config;
+
+ // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig.
+ AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+ int64_t matcher1Id = matcher1.id();
+ *config.add_atom_matcher() = matcher1;
+
+ AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+ int64_t matcher2Id = matcher2.id();
+ *config.add_atom_matcher() = matcher2;
+
+ AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
+ int64_t matcher3Id = matcher3.id();
+ *config.add_atom_matcher() = matcher3;
+
+ AtomMatcher matcher4 = CreateTemperatureAtomMatcher();
+ int64_t matcher4Id = matcher4.id();
+ *config.add_atom_matcher() = matcher4;
+
+ AtomMatcher matcher5 = CreateSimpleAtomMatcher("SubsystemSleep", util::SUBSYSTEM_SLEEP_STATE);
+ int64_t matcher5Id = matcher5.id();
+ *config.add_atom_matcher() = matcher5;
+
+ Predicate predicate1 = CreateScreenIsOnPredicate();
+ int64_t predicate1Id = predicate1.id();
+ *config.add_predicate() = predicate1;
+
+ // Add a few gauge metrics.
+ // Will be preserved.
+ GaugeMetric gauge1 = createGaugeMetric("GAUGE1", matcher4Id, GaugeMetric::FIRST_N_SAMPLES,
+ predicate1Id, matcher1Id);
+ int64_t gauge1Id = gauge1.id();
+ *config.add_gauge_metric() = gauge1;
+
+ // Will be replaced.
+ GaugeMetric gauge2 =
+ createGaugeMetric("GAUGE2", matcher1Id, GaugeMetric::FIRST_N_SAMPLES, nullopt, nullopt);
+ int64_t gauge2Id = gauge2.id();
+ *config.add_gauge_metric() = gauge2;
+
+ // Will be replaced.
+ GaugeMetric gauge3 = createGaugeMetric("GAUGE3", matcher5Id, GaugeMetric::FIRST_N_SAMPLES,
+ nullopt, matcher3Id);
+ int64_t gauge3Id = gauge3.id();
+ *config.add_gauge_metric() = gauge3;
+
+ // Will be replaced.
+ GaugeMetric gauge4 = createGaugeMetric("GAUGE4", matcher3Id, GaugeMetric::RANDOM_ONE_SAMPLE,
+ predicate1Id, nullopt);
+ int64_t gauge4Id = gauge4.id();
+ *config.add_gauge_metric() = gauge4;
+
+ // Will be deleted.
+ GaugeMetric gauge5 =
+ createGaugeMetric("GAUGE5", matcher2Id, GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, {});
+ int64_t gauge5Id = gauge5.id();
+ *config.add_gauge_metric() = gauge5;
+
+ EXPECT_TRUE(initConfig(config));
+
+ // Used later to ensure the condition wizard is replaced. Get it before doing the update.
+ sp<EventMatcherWizard> oldMatcherWizard =
+ static_cast<GaugeMetricProducer*>(oldMetricProducers[0].get())->mEventMatcherWizard;
+ EXPECT_EQ(oldMatcherWizard->getStrongCount(), 6);
+
+ // Change gauge2, causing it to be replaced.
+ gauge2.set_max_num_gauge_atoms_per_bucket(50);
+
+ // Mark matcher 3 as replaced. Causes gauge3 and gauge4 to be replaced.
+ set<int64_t> replacedMatchers = {matcher3Id};
+
+ // New gauge metric.
+ GaugeMetric gauge6 = createGaugeMetric("GAUGE6", matcher5Id, GaugeMetric::FIRST_N_SAMPLES,
+ predicate1Id, matcher3Id);
+ int64_t gauge6Id = gauge6.id();
+
+ // Map the matchers and predicates in reverse order to force the indices to change.
+ std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+ const int matcher5Index = 0;
+ newAtomMatchingTrackerMap[matcher5Id] = 0;
+ const int matcher4Index = 1;
+ newAtomMatchingTrackerMap[matcher4Id] = 1;
+ const int matcher3Index = 2;
+ newAtomMatchingTrackerMap[matcher3Id] = 2;
+ const int matcher2Index = 3;
+ newAtomMatchingTrackerMap[matcher2Id] = 3;
+ const int matcher1Index = 4;
+ newAtomMatchingTrackerMap[matcher1Id] = 4;
+ // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+ vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(5);
+ std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
+ newAtomMatchingTrackers.begin());
+
+ std::unordered_map<int64_t, int> newConditionTrackerMap;
+ const int predicate1Index = 0;
+ newConditionTrackerMap[predicate1Id] = 0;
+ // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them.
+ vector<sp<ConditionTracker>> newConditionTrackers(1);
+ std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
+ newConditionTrackers.begin());
+ // Say that predicate1 is unknown since the initial condition never changed.
+ vector<ConditionState> conditionCache = {ConditionState::kUnknown};
+
+ StatsdConfig newConfig;
+ *newConfig.add_gauge_metric() = gauge6;
+ const int gauge6Index = 0;
+ *newConfig.add_gauge_metric() = gauge3;
+ const int gauge3Index = 1;
+ *newConfig.add_gauge_metric() = gauge1;
+ const int gauge1Index = 2;
+ *newConfig.add_gauge_metric() = gauge4;
+ const int gauge4Index = 3;
+ *newConfig.add_gauge_metric() = gauge2;
+ const int gauge2Index = 4;
+
+ // Output data structures to validate.
+ unordered_map<int64_t, int> newMetricProducerMap;
+ vector<sp<MetricProducer>> newMetricProducers;
+ unordered_map<int, vector<int>> conditionToMetricMap;
+ unordered_map<int, vector<int>> trackerToMetricMap;
+ set<int64_t> noReportMetricIds;
+ unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+ vector<int> metricsWithActivation;
+ set<int64_t> replacedMetrics;
+ EXPECT_TRUE(updateMetrics(
+ key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
+ newAtomMatchingTrackers, newConditionTrackerMap, /*replacedConditions=*/{},
+ newConditionTrackers, conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{},
+ /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
+ newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, replacedMetrics));
+
+ unordered_map<int64_t, int> expectedMetricProducerMap = {
+ {gauge1Id, gauge1Index}, {gauge2Id, gauge2Index}, {gauge3Id, gauge3Index},
+ {gauge4Id, gauge4Index}, {gauge6Id, gauge6Index},
+ };
+ EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+ EXPECT_EQ(replacedMetrics, set<int64_t>({gauge2Id, gauge3Id, gauge4Id}));
+
+ // Make sure preserved metrics are the same.
+ ASSERT_EQ(newMetricProducers.size(), 5);
+ EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(gauge1Id)],
+ newMetricProducers[newMetricProducerMap.at(gauge1Id)]);
+
+ // Make sure replaced metrics are different.
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gauge2Id)],
+ newMetricProducers[newMetricProducerMap.at(gauge2Id)]);
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gauge3Id)],
+ newMetricProducers[newMetricProducerMap.at(gauge3Id)]);
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gauge4Id)],
+ newMetricProducers[newMetricProducerMap.at(gauge4Id)]);
+
+ // Verify the conditionToMetricMap.
+ ASSERT_EQ(conditionToMetricMap.size(), 1);
+ const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
+ EXPECT_THAT(condition1Metrics, UnorderedElementsAre(gauge1Index, gauge4Index, gauge6Index));
+
+ // Verify the trackerToMetricMap.
+ ASSERT_EQ(trackerToMetricMap.size(), 4);
+ const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
+ EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(gauge1Index, gauge2Index));
+ const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
+ EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(gauge3Index, gauge4Index, gauge6Index));
+ const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index];
+ EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(gauge1Index));
+ const vector<int>& matcher5Metrics = trackerToMetricMap[matcher5Index];
+ EXPECT_THAT(matcher5Metrics, UnorderedElementsAre(gauge3Index, gauge6Index));
+
+ // Verify event activation/deactivation maps.
+ ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
+ ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
+ ASSERT_EQ(metricsWithActivation.size(), 0);
+
+ // Verify tracker indices/ids/conditions/states are correct.
+ GaugeMetricProducer* gaugeProducer1 =
+ static_cast<GaugeMetricProducer*>(newMetricProducers[gauge1Index].get());
+ EXPECT_EQ(gaugeProducer1->getMetricId(), gauge1Id);
+ EXPECT_EQ(gaugeProducer1->mConditionTrackerIndex, predicate1Index);
+ EXPECT_EQ(gaugeProducer1->mCondition, ConditionState::kUnknown);
+ EXPECT_EQ(gaugeProducer1->mWhatMatcherIndex, matcher4Index);
+ GaugeMetricProducer* gaugeProducer2 =
+ static_cast<GaugeMetricProducer*>(newMetricProducers[gauge2Index].get());
+ EXPECT_EQ(gaugeProducer2->getMetricId(), gauge2Id);
+ EXPECT_EQ(gaugeProducer2->mConditionTrackerIndex, -1);
+ EXPECT_EQ(gaugeProducer2->mCondition, ConditionState::kTrue);
+ EXPECT_EQ(gaugeProducer2->mWhatMatcherIndex, matcher1Index);
+ GaugeMetricProducer* gaugeProducer3 =
+ static_cast<GaugeMetricProducer*>(newMetricProducers[gauge3Index].get());
+ EXPECT_EQ(gaugeProducer3->getMetricId(), gauge3Id);
+ EXPECT_EQ(gaugeProducer3->mConditionTrackerIndex, -1);
+ EXPECT_EQ(gaugeProducer3->mCondition, ConditionState::kTrue);
+ EXPECT_EQ(gaugeProducer3->mWhatMatcherIndex, matcher5Index);
+ GaugeMetricProducer* gaugeProducer4 =
+ static_cast<GaugeMetricProducer*>(newMetricProducers[gauge4Index].get());
+ EXPECT_EQ(gaugeProducer4->getMetricId(), gauge4Id);
+ EXPECT_EQ(gaugeProducer4->mConditionTrackerIndex, predicate1Index);
+ EXPECT_EQ(gaugeProducer4->mCondition, ConditionState::kUnknown);
+ EXPECT_EQ(gaugeProducer4->mWhatMatcherIndex, matcher3Index);
+ GaugeMetricProducer* gaugeProducer6 =
+ static_cast<GaugeMetricProducer*>(newMetricProducers[gauge6Index].get());
+ EXPECT_EQ(gaugeProducer6->getMetricId(), gauge6Id);
+ EXPECT_EQ(gaugeProducer6->mConditionTrackerIndex, predicate1Index);
+ EXPECT_EQ(gaugeProducer6->mCondition, ConditionState::kUnknown);
+ EXPECT_EQ(gaugeProducer6->mWhatMatcherIndex, matcher5Index);
+
+ sp<EventMatcherWizard> newMatcherWizard = gaugeProducer1->mEventMatcherWizard;
+ EXPECT_NE(newMatcherWizard, oldMatcherWizard);
+ EXPECT_EQ(newMatcherWizard->getStrongCount(), 6);
+ oldMetricProducers.clear();
+ // Only reference to the old wizard should be the one in the test.
+ EXPECT_EQ(oldMatcherWizard->getStrongCount(), 1);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateDurationMetrics) {
+ StatsdConfig config;
+ // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig.
+ AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+ int64_t matcher1Id = matcher1.id();
+ *config.add_atom_matcher() = matcher1;
+
+ AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+ int64_t matcher2Id = matcher2.id();
+ *config.add_atom_matcher() = matcher2;
+
+ AtomMatcher matcher3 = CreateAcquireWakelockAtomMatcher();
+ int64_t matcher3Id = matcher3.id();
+ *config.add_atom_matcher() = matcher3;
+
+ AtomMatcher matcher4 = CreateReleaseWakelockAtomMatcher();
+ int64_t matcher4Id = matcher4.id();
+ *config.add_atom_matcher() = matcher4;
+
+ AtomMatcher matcher5 = CreateMoveToForegroundAtomMatcher();
+ int64_t matcher5Id = matcher5.id();
+ *config.add_atom_matcher() = matcher5;
+
+ AtomMatcher matcher6 = CreateMoveToBackgroundAtomMatcher();
+ int64_t matcher6Id = matcher6.id();
+ *config.add_atom_matcher() = matcher6;
+
+ AtomMatcher matcher7 = CreateBatteryStateNoneMatcher();
+ int64_t matcher7Id = matcher7.id();
+ *config.add_atom_matcher() = matcher7;
+
+ AtomMatcher matcher8 = CreateBatteryStateUsbMatcher();
+ int64_t matcher8Id = matcher8.id();
+ *config.add_atom_matcher() = matcher8;
+
+ Predicate predicate1 = CreateScreenIsOnPredicate();
+ int64_t predicate1Id = predicate1.id();
+ *config.add_predicate() = predicate1;
+
+ Predicate predicate2 = CreateScreenIsOffPredicate();
+ int64_t predicate2Id = predicate2.id();
+ *config.add_predicate() = predicate2;
+
+ Predicate predicate3 = CreateDeviceUnpluggedPredicate();
+ int64_t predicate3Id = predicate3.id();
+ *config.add_predicate() = predicate3;
+
+ Predicate predicate4 = CreateIsInBackgroundPredicate();
+ *predicate4.mutable_simple_predicate()->mutable_dimensions() =
+ CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1});
+ int64_t predicate4Id = predicate4.id();
+ *config.add_predicate() = predicate4;
+
+ Predicate predicate5 = CreateHoldingWakelockPredicate();
+ *predicate5.mutable_simple_predicate()->mutable_dimensions() =
+ CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ predicate5.mutable_simple_predicate()->set_stop_all(matcher7Id);
+ int64_t predicate5Id = predicate5.id();
+ *config.add_predicate() = predicate5;
+
+ State state1 = CreateScreenStateWithOnOffMap(0x123, 0x321);
+ int64_t state1Id = state1.id();
+ *config.add_state() = state1;
+
+ State state2 = CreateScreenState();
+ int64_t state2Id = state2.id();
+ *config.add_state() = state2;
+
+ // Add a few duration metrics.
+ // Will be preserved.
+ DurationMetric duration1 =
+ createDurationMetric("DURATION1", predicate5Id, predicate4Id, {state2Id});
+ *duration1.mutable_dimensions_in_what() =
+ CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ MetricConditionLink* link = duration1.add_links();
+ link->set_condition(predicate4Id);
+ *link->mutable_fields_in_what() =
+ CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ *link->mutable_fields_in_condition() =
+ CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1} /*uid field*/);
+ int64_t duration1Id = duration1.id();
+ *config.add_duration_metric() = duration1;
+
+ // Will be replaced.
+ DurationMetric duration2 = createDurationMetric("DURATION2", predicate1Id, nullopt, {});
+ int64_t duration2Id = duration2.id();
+ *config.add_duration_metric() = duration2;
+
+ // Will be replaced.
+ DurationMetric duration3 = createDurationMetric("DURATION3", predicate3Id, nullopt, {state1Id});
+ int64_t duration3Id = duration3.id();
+ *config.add_duration_metric() = duration3;
+
+ // Will be replaced.
+ DurationMetric duration4 = createDurationMetric("DURATION4", predicate3Id, predicate2Id, {});
+ int64_t duration4Id = duration4.id();
+ *config.add_duration_metric() = duration4;
+
+ // Will be deleted.
+ DurationMetric duration5 = createDurationMetric("DURATION5", predicate2Id, nullopt, {});
+ int64_t duration5Id = duration5.id();
+ *config.add_duration_metric() = duration5;
+
+ EXPECT_TRUE(initConfig(config));
+
+ // Make some sliced conditions true.
+ int uid1 = 10;
+ int uid2 = 11;
+ vector<MatchingState> matchingStates(8, MatchingState::kNotMatched);
+ matchingStates[2] = kMatched;
+ vector<ConditionState> conditionCache(5, ConditionState::kNotEvaluated);
+ vector<bool> changedCache(5, false);
+ unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(timeBaseNs + 3, {uid1}, {"tag"}, "wl1");
+ oldConditionTrackers[4]->evaluateCondition(*event.get(), matchingStates, oldConditionTrackers,
+ conditionCache, changedCache);
+ EXPECT_TRUE(oldConditionTrackers[4]->isSliced());
+ EXPECT_TRUE(changedCache[4]);
+ EXPECT_EQ(conditionCache[4], ConditionState::kTrue);
+ oldMetricProducers[0]->onMatchedLogEvent(2, *event.get());
+
+ fill(conditionCache.begin(), conditionCache.end(), ConditionState::kNotEvaluated);
+ fill(changedCache.begin(), changedCache.end(), false);
+ event = CreateAcquireWakelockEvent(timeBaseNs + 3, {uid2}, {"tag"}, "wl2");
+ oldConditionTrackers[4]->evaluateCondition(*event.get(), matchingStates, oldConditionTrackers,
+ conditionCache, changedCache);
+ EXPECT_TRUE(changedCache[4]);
+ EXPECT_EQ(conditionCache[4], ConditionState::kTrue);
+ oldMetricProducers[0]->onMatchedLogEvent(2, *event.get());
+
+ // Used later to ensure the condition wizard is replaced. Get it before doing the update.
+ // The duration trackers have a pointer to the wizard, and 2 trackers were created above.
+ sp<ConditionWizard> oldConditionWizard = oldMetricProducers[0]->mWizard;
+ EXPECT_EQ(oldConditionWizard->getStrongCount(), 8);
+
+ // Replace predicate1, predicate3, and state1. Causes duration2/3/4 to be replaced.
+ set<int64_t> replacedConditions({predicate1Id, predicate2Id});
+ set<int64_t> replacedStates({state1Id});
+
+ // New duration metric.
+ DurationMetric duration6 = createDurationMetric("DURATION6", predicate4Id, predicate5Id, {});
+ *duration6.mutable_dimensions_in_what() =
+ CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1} /*uid field*/);
+ link = duration6.add_links();
+ link->set_condition(predicate5Id);
+ *link->mutable_fields_in_what() =
+ CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1} /*uid field*/);
+ *link->mutable_fields_in_condition() =
+ CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ int64_t duration6Id = duration6.id();
+
+ // Map the matchers and predicates in reverse order to force the indices to change.
+ const int matcher8Index = 0, matcher7Index = 1, matcher6Index = 2, matcher5Index = 3,
+ matcher4Index = 4, matcher3Index = 5, matcher2Index = 6, matcher1Index = 7;
+ std::unordered_map<int64_t, int> newAtomMatchingTrackerMap({{matcher8Id, matcher8Index},
+ {matcher7Id, matcher7Index},
+ {matcher6Id, matcher6Index},
+ {matcher5Id, matcher5Index},
+ {matcher4Id, matcher4Index},
+ {matcher3Id, matcher3Index},
+ {matcher2Id, matcher2Index},
+ {matcher1Id, matcher1Index}});
+ // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+ vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(8);
+ reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
+ newAtomMatchingTrackers.begin());
+
+ const int predicate5Index = 0, predicate4Index = 1, predicate3Index = 2, predicate2Index = 3,
+ predicate1Index = 4;
+ std::unordered_map<int64_t, int> newConditionTrackerMap({
+ {predicate5Id, predicate5Index},
+ {predicate4Id, predicate4Index},
+ {predicate3Id, predicate3Index},
+ {predicate2Id, predicate2Index},
+ {predicate1Id, predicate1Index},
+ });
+ // Use the existing conditionTrackers and reinitialize them to get the initial condition cache.
+ vector<sp<ConditionTracker>> newConditionTrackers(5);
+ reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
+ newConditionTrackers.begin());
+ vector<Predicate> conditionProtos(5);
+ reverse_copy(config.predicate().begin(), config.predicate().end(), conditionProtos.begin());
+ for (int i = 0; i < newConditionTrackers.size(); i++) {
+ EXPECT_TRUE(newConditionTrackers[i]->onConfigUpdated(
+ conditionProtos, i, newConditionTrackers, newAtomMatchingTrackerMap,
+ newConditionTrackerMap));
+ }
+ vector<bool> cycleTracker(5, false);
+ fill(conditionCache.begin(), conditionCache.end(), ConditionState::kNotEvaluated);
+ for (int i = 0; i < newConditionTrackers.size(); i++) {
+ EXPECT_TRUE(newConditionTrackers[i]->init(conditionProtos, newConditionTrackers,
+ newConditionTrackerMap, cycleTracker,
+ conditionCache));
+ }
+ // Predicate5 should be true since 2 uids have wakelocks
+ EXPECT_EQ(conditionCache, vector({kTrue, kUnknown, kUnknown, kUnknown, kUnknown}));
+
+ StatsdConfig newConfig;
+ *newConfig.add_duration_metric() = duration6;
+ const int duration6Index = 0;
+ *newConfig.add_duration_metric() = duration3;
+ const int duration3Index = 1;
+ *newConfig.add_duration_metric() = duration1;
+ const int duration1Index = 2;
+ *newConfig.add_duration_metric() = duration4;
+ const int duration4Index = 3;
+ *newConfig.add_duration_metric() = duration2;
+ const int duration2Index = 4;
+
+ for (const Predicate& predicate : conditionProtos) {
+ *newConfig.add_predicate() = predicate;
+ }
+ *newConfig.add_state() = state1;
+ *newConfig.add_state() = state2;
+ unordered_map<int64_t, int> stateAtomIdMap;
+ unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
+ map<int64_t, uint64_t> stateProtoHashes;
+ EXPECT_TRUE(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes));
+
+ // Output data structures to validate.
+ unordered_map<int64_t, int> newMetricProducerMap;
+ vector<sp<MetricProducer>> newMetricProducers;
+ unordered_map<int, vector<int>> conditionToMetricMap;
+ unordered_map<int, vector<int>> trackerToMetricMap;
+ set<int64_t> noReportMetricIds;
+ unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+ vector<int> metricsWithActivation;
+ set<int64_t> replacedMetrics;
+ EXPECT_TRUE(updateMetrics(
+ key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, /*replacedMatchers=*/{},
+ newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions,
+ newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates,
+ oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, newMetricProducers,
+ conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, replacedMetrics));
+
+ unordered_map<int64_t, int> expectedMetricProducerMap = {
+ {duration1Id, duration1Index}, {duration2Id, duration2Index},
+ {duration3Id, duration3Index}, {duration4Id, duration4Index},
+ {duration6Id, duration6Index},
+ };
+ EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+ EXPECT_EQ(replacedMetrics, set<int64_t>({duration2Id, duration3Id, duration4Id}));
+ // Make sure preserved metrics are the same.
+ ASSERT_EQ(newMetricProducers.size(), 5);
+ EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(duration1Id)],
+ newMetricProducers[newMetricProducerMap.at(duration1Id)]);
+
+ // Make sure replaced metrics are different.
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(duration2Id)],
+ newMetricProducers[newMetricProducerMap.at(duration2Id)]);
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(duration3Id)],
+ newMetricProducers[newMetricProducerMap.at(duration3Id)]);
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(duration4Id)],
+ newMetricProducers[newMetricProducerMap.at(duration4Id)]);
+
+ // Verify the conditionToMetricMap. Note that the "what" is not in this map.
+ ASSERT_EQ(conditionToMetricMap.size(), 3);
+ const vector<int>& condition2Metrics = conditionToMetricMap[predicate2Index];
+ EXPECT_THAT(condition2Metrics, UnorderedElementsAre(duration4Index));
+ const vector<int>& condition4Metrics = conditionToMetricMap[predicate4Index];
+ EXPECT_THAT(condition4Metrics, UnorderedElementsAre(duration1Index));
+ const vector<int>& condition5Metrics = conditionToMetricMap[predicate5Index];
+ EXPECT_THAT(condition5Metrics, UnorderedElementsAre(duration6Index));
+
+ // Verify the trackerToMetricMap. The start/stop/stopall indices from the "what" should be here.
+ ASSERT_EQ(trackerToMetricMap.size(), 8);
+ const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
+ EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(duration2Index));
+ const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index];
+ EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(duration2Index));
+ const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
+ EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(duration1Index));
+ const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index];
+ EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(duration1Index));
+ const vector<int>& matcher5Metrics = trackerToMetricMap[matcher5Index];
+ EXPECT_THAT(matcher5Metrics, UnorderedElementsAre(duration6Index));
+ const vector<int>& matcher6Metrics = trackerToMetricMap[matcher6Index];
+ EXPECT_THAT(matcher6Metrics, UnorderedElementsAre(duration6Index));
+ const vector<int>& matcher7Metrics = trackerToMetricMap[matcher7Index];
+ EXPECT_THAT(matcher7Metrics,
+ UnorderedElementsAre(duration1Index, duration3Index, duration4Index));
+ const vector<int>& matcher8Metrics = trackerToMetricMap[matcher8Index];
+ EXPECT_THAT(matcher8Metrics, UnorderedElementsAre(duration3Index, duration4Index));
+
+ // Verify event activation/deactivation maps.
+ ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
+ ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
+ ASSERT_EQ(metricsWithActivation.size(), 0);
+
+ // Verify tracker indices/ids/conditions are correct.
+ DurationMetricProducer* durationProducer1 =
+ static_cast<DurationMetricProducer*>(newMetricProducers[duration1Index].get());
+ EXPECT_EQ(durationProducer1->getMetricId(), duration1Id);
+ EXPECT_EQ(durationProducer1->mConditionTrackerIndex, predicate4Index);
+ EXPECT_EQ(durationProducer1->mCondition, ConditionState::kUnknown);
+ EXPECT_EQ(durationProducer1->mStartIndex, matcher3Index);
+ EXPECT_EQ(durationProducer1->mStopIndex, matcher4Index);
+ EXPECT_EQ(durationProducer1->mStopAllIndex, matcher7Index);
+ EXPECT_EQ(durationProducer1->mCurrentSlicedDurationTrackerMap.size(), 2);
+ for (const auto& durationTrackerIt : durationProducer1->mCurrentSlicedDurationTrackerMap) {
+ EXPECT_EQ(durationTrackerIt.second->mConditionTrackerIndex, predicate4Index);
+ }
+ DurationMetricProducer* durationProducer2 =
+ static_cast<DurationMetricProducer*>(newMetricProducers[duration2Index].get());
+ EXPECT_EQ(durationProducer2->getMetricId(), duration2Id);
+ EXPECT_EQ(durationProducer2->mConditionTrackerIndex, -1);
+ EXPECT_EQ(durationProducer2->mCondition, ConditionState::kTrue);
+ EXPECT_EQ(durationProducer2->mStartIndex, matcher1Index);
+ EXPECT_EQ(durationProducer2->mStopIndex, matcher2Index);
+ EXPECT_EQ(durationProducer2->mStopAllIndex, -1);
+ DurationMetricProducer* durationProducer3 =
+ static_cast<DurationMetricProducer*>(newMetricProducers[duration3Index].get());
+ EXPECT_EQ(durationProducer3->getMetricId(), duration3Id);
+ EXPECT_EQ(durationProducer3->mConditionTrackerIndex, -1);
+ EXPECT_EQ(durationProducer3->mCondition, ConditionState::kTrue);
+ EXPECT_EQ(durationProducer3->mStartIndex, matcher7Index);
+ EXPECT_EQ(durationProducer3->mStopIndex, matcher8Index);
+ EXPECT_EQ(durationProducer3->mStopAllIndex, -1);
+ DurationMetricProducer* durationProducer4 =
+ static_cast<DurationMetricProducer*>(newMetricProducers[duration4Index].get());
+ EXPECT_EQ(durationProducer4->getMetricId(), duration4Id);
+ EXPECT_EQ(durationProducer4->mConditionTrackerIndex, predicate2Index);
+ EXPECT_EQ(durationProducer4->mCondition, ConditionState::kUnknown);
+ EXPECT_EQ(durationProducer4->mStartIndex, matcher7Index);
+ EXPECT_EQ(durationProducer4->mStopIndex, matcher8Index);
+ EXPECT_EQ(durationProducer4->mStopAllIndex, -1);
+ DurationMetricProducer* durationProducer6 =
+ static_cast<DurationMetricProducer*>(newMetricProducers[duration6Index].get());
+ EXPECT_EQ(durationProducer6->getMetricId(), duration6Id);
+ EXPECT_EQ(durationProducer6->mConditionTrackerIndex, predicate5Index);
+ // TODO(b/167491517): should this be unknown since the condition is sliced?
+ EXPECT_EQ(durationProducer6->mCondition, ConditionState::kTrue);
+ EXPECT_EQ(durationProducer6->mStartIndex, matcher6Index);
+ EXPECT_EQ(durationProducer6->mStopIndex, matcher5Index);
+ EXPECT_EQ(durationProducer6->mStopAllIndex, -1);
+
+ sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard;
+ EXPECT_NE(newConditionWizard, oldConditionWizard);
+ EXPECT_EQ(newConditionWizard->getStrongCount(), 8);
+ oldMetricProducers.clear();
+ // Only reference to the old wizard should be the one in the test.
+ EXPECT_EQ(oldConditionWizard->getStrongCount(), 1);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateValueMetrics) {
+ StatsdConfig config;
+
+ // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig.
+ AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+ int64_t matcher1Id = matcher1.id();
+ *config.add_atom_matcher() = matcher1;
+
+ AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+ int64_t matcher2Id = matcher2.id();
+ *config.add_atom_matcher() = matcher2;
+
+ AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
+ int64_t matcher3Id = matcher3.id();
+ *config.add_atom_matcher() = matcher3;
+
+ AtomMatcher matcher4 = CreateTemperatureAtomMatcher();
+ int64_t matcher4Id = matcher4.id();
+ *config.add_atom_matcher() = matcher4;
+
+ AtomMatcher matcher5 = CreateSimpleAtomMatcher("SubsystemSleep", util::SUBSYSTEM_SLEEP_STATE);
+ int64_t matcher5Id = matcher5.id();
+ *config.add_atom_matcher() = matcher5;
+
+ Predicate predicate1 = CreateScreenIsOnPredicate();
+ int64_t predicate1Id = predicate1.id();
+ *config.add_predicate() = predicate1;
+
+ Predicate predicate2 = CreateScreenIsOffPredicate();
+ int64_t predicate2Id = predicate2.id();
+ *config.add_predicate() = predicate2;
+
+ State state1 = CreateScreenStateWithOnOffMap(0x123, 0x321);
+ int64_t state1Id = state1.id();
+ *config.add_state() = state1;
+
+ State state2 = CreateScreenState();
+ int64_t state2Id = state2.id();
+ *config.add_state() = state2;
+
+ // Add a few value metrics.
+ // Note that these will not work as "real" metrics since the value field is always 2.
+ // Will be preserved.
+ ValueMetric value1 = createValueMetric("VALUE1", matcher4, predicate1Id, {state1Id});
+ int64_t value1Id = value1.id();
+ *config.add_value_metric() = value1;
+
+ // Will be replaced - definition change.
+ ValueMetric value2 = createValueMetric("VALUE2", matcher1, nullopt, {});
+ int64_t value2Id = value2.id();
+ *config.add_value_metric() = value2;
+
+ // Will be replaced - condition change.
+ ValueMetric value3 = createValueMetric("VALUE3", matcher5, predicate2Id, {});
+ int64_t value3Id = value3.id();
+ *config.add_value_metric() = value3;
+
+ // Will be replaced - state change.
+ ValueMetric value4 = createValueMetric("VALUE4", matcher3, nullopt, {state2Id});
+ int64_t value4Id = value4.id();
+ *config.add_value_metric() = value4;
+
+ // Will be deleted.
+ ValueMetric value5 = createValueMetric("VALUE5", matcher2, nullopt, {});
+ int64_t value5Id = value5.id();
+ *config.add_value_metric() = value5;
+
+ EXPECT_TRUE(initConfig(config));
+
+ // Used later to ensure the condition wizard is replaced. Get it before doing the update.
+ sp<EventMatcherWizard> oldMatcherWizard =
+ static_cast<ValueMetricProducer*>(oldMetricProducers[0].get())->mEventMatcherWizard;
+ EXPECT_EQ(oldMatcherWizard->getStrongCount(), 6);
+
+ // Change value2, causing it to be replaced.
+ value2.set_aggregation_type(ValueMetric::AVG);
+
+ // Mark predicate 2 as replaced. Causes value3 to be replaced.
+ set<int64_t> replacedConditions = {predicate2Id};
+
+ // Mark state 2 as replaced. Causes value4 to be replaced.
+ set<int64_t> replacedStates = {state2Id};
+
+ // New value metric.
+ ValueMetric value6 = createValueMetric("VALUE6", matcher5, predicate1Id, {state1Id});
+ int64_t value6Id = value6.id();
+
+ // Map the matchers and predicates in reverse order to force the indices to change.
+ std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+ const int matcher5Index = 0;
+ newAtomMatchingTrackerMap[matcher5Id] = 0;
+ const int matcher4Index = 1;
+ newAtomMatchingTrackerMap[matcher4Id] = 1;
+ const int matcher3Index = 2;
+ newAtomMatchingTrackerMap[matcher3Id] = 2;
+ const int matcher2Index = 3;
+ newAtomMatchingTrackerMap[matcher2Id] = 3;
+ const int matcher1Index = 4;
+ newAtomMatchingTrackerMap[matcher1Id] = 4;
+ // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+ vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(5);
+ std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
+ newAtomMatchingTrackers.begin());
+
+ std::unordered_map<int64_t, int> newConditionTrackerMap;
+ const int predicate2Index = 0;
+ newConditionTrackerMap[predicate2Id] = 0;
+ const int predicate1Index = 1;
+ newConditionTrackerMap[predicate1Id] = 1;
+ // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them.
+ vector<sp<ConditionTracker>> newConditionTrackers(2);
+ std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
+ newConditionTrackers.begin());
+ // Say that predicate1 & predicate2 is unknown since the initial condition never changed.
+ vector<ConditionState> conditionCache = {ConditionState::kUnknown, ConditionState::kUnknown};
+
+ StatsdConfig newConfig;
+ *newConfig.add_value_metric() = value6;
+ const int value6Index = 0;
+ *newConfig.add_value_metric() = value3;
+ const int value3Index = 1;
+ *newConfig.add_value_metric() = value1;
+ const int value1Index = 2;
+ *newConfig.add_value_metric() = value4;
+ const int value4Index = 3;
+ *newConfig.add_value_metric() = value2;
+ const int value2Index = 4;
+
+ *newConfig.add_state() = state1;
+ *newConfig.add_state() = state2;
+
+ unordered_map<int64_t, int> stateAtomIdMap;
+ unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
+ map<int64_t, uint64_t> stateProtoHashes;
+ EXPECT_TRUE(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes));
+
+ // Output data structures to validate.
+ unordered_map<int64_t, int> newMetricProducerMap;
+ vector<sp<MetricProducer>> newMetricProducers;
+ unordered_map<int, vector<int>> conditionToMetricMap;
+ unordered_map<int, vector<int>> trackerToMetricMap;
+ set<int64_t> noReportMetricIds;
+ unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+ vector<int> metricsWithActivation;
+ set<int64_t> replacedMetrics;
+ EXPECT_TRUE(updateMetrics(
+ key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, /*replacedMatchers=*/{},
+ newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions,
+ newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates,
+ oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, newMetricProducers,
+ conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, replacedMetrics));
+
+ unordered_map<int64_t, int> expectedMetricProducerMap = {
+ {value1Id, value1Index}, {value2Id, value2Index}, {value3Id, value3Index},
+ {value4Id, value4Index}, {value6Id, value6Index},
+ };
+ EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+ EXPECT_EQ(replacedMetrics, set<int64_t>({value2Id, value3Id, value4Id}));
+
+ // Make sure preserved metrics are the same.
+ ASSERT_EQ(newMetricProducers.size(), 5);
+ EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(value1Id)],
+ newMetricProducers[newMetricProducerMap.at(value1Id)]);
+
+ // Make sure replaced metrics are different.
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(value2Id)],
+ newMetricProducers[newMetricProducerMap.at(value2Id)]);
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(value3Id)],
+ newMetricProducers[newMetricProducerMap.at(value3Id)]);
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(value4Id)],
+ newMetricProducers[newMetricProducerMap.at(value4Id)]);
+
+ // Verify the conditionToMetricMap.
+ ASSERT_EQ(conditionToMetricMap.size(), 2);
+ const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
+ EXPECT_THAT(condition1Metrics, UnorderedElementsAre(value1Index, value6Index));
+ const vector<int>& condition2Metrics = conditionToMetricMap[predicate2Index];
+ EXPECT_THAT(condition2Metrics, UnorderedElementsAre(value3Index));
+
+ // Verify the trackerToMetricMap.
+ ASSERT_EQ(trackerToMetricMap.size(), 4);
+ const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
+ EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(value2Index));
+ const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
+ EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(value4Index));
+ const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index];
+ EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(value1Index));
+ const vector<int>& matcher5Metrics = trackerToMetricMap[matcher5Index];
+ EXPECT_THAT(matcher5Metrics, UnorderedElementsAre(value3Index, value6Index));
+
+ // Verify event activation/deactivation maps.
+ ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
+ ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
+ ASSERT_EQ(metricsWithActivation.size(), 0);
+
+ // Verify tracker indices/ids/conditions/states are correct.
+ ValueMetricProducer* valueProducer1 =
+ static_cast<ValueMetricProducer*>(newMetricProducers[value1Index].get());
+ EXPECT_EQ(valueProducer1->getMetricId(), value1Id);
+ EXPECT_EQ(valueProducer1->mConditionTrackerIndex, predicate1Index);
+ EXPECT_EQ(valueProducer1->mCondition, ConditionState::kUnknown);
+ EXPECT_EQ(valueProducer1->mWhatMatcherIndex, matcher4Index);
+ ValueMetricProducer* valueProducer2 =
+ static_cast<ValueMetricProducer*>(newMetricProducers[value2Index].get());
+ EXPECT_EQ(valueProducer2->getMetricId(), value2Id);
+ EXPECT_EQ(valueProducer2->mConditionTrackerIndex, -1);
+ EXPECT_EQ(valueProducer2->mCondition, ConditionState::kTrue);
+ EXPECT_EQ(valueProducer2->mWhatMatcherIndex, matcher1Index);
+ ValueMetricProducer* valueProducer3 =
+ static_cast<ValueMetricProducer*>(newMetricProducers[value3Index].get());
+ EXPECT_EQ(valueProducer3->getMetricId(), value3Id);
+ EXPECT_EQ(valueProducer3->mConditionTrackerIndex, predicate2Index);
+ EXPECT_EQ(valueProducer3->mCondition, ConditionState::kUnknown);
+ EXPECT_EQ(valueProducer3->mWhatMatcherIndex, matcher5Index);
+ ValueMetricProducer* valueProducer4 =
+ static_cast<ValueMetricProducer*>(newMetricProducers[value4Index].get());
+ EXPECT_EQ(valueProducer4->getMetricId(), value4Id);
+ EXPECT_EQ(valueProducer4->mConditionTrackerIndex, -1);
+ EXPECT_EQ(valueProducer4->mCondition, ConditionState::kTrue);
+ EXPECT_EQ(valueProducer4->mWhatMatcherIndex, matcher3Index);
+ ValueMetricProducer* valueProducer6 =
+ static_cast<ValueMetricProducer*>(newMetricProducers[value6Index].get());
+ EXPECT_EQ(valueProducer6->getMetricId(), value6Id);
+ EXPECT_EQ(valueProducer6->mConditionTrackerIndex, predicate1Index);
+ EXPECT_EQ(valueProducer6->mCondition, ConditionState::kUnknown);
+ EXPECT_EQ(valueProducer6->mWhatMatcherIndex, matcher5Index);
+
+ sp<EventMatcherWizard> newMatcherWizard = valueProducer1->mEventMatcherWizard;
+ EXPECT_NE(newMatcherWizard, oldMatcherWizard);
+ EXPECT_EQ(newMatcherWizard->getStrongCount(), 6);
+ oldMetricProducers.clear();
+ // Only reference to the old wizard should be the one in the test.
+ EXPECT_EQ(oldMatcherWizard->getStrongCount(), 1);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateMetricActivations) {
+ StatsdConfig config;
+ // Add atom matchers
+ AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+ int64_t matcher1Id = matcher1.id();
+ *config.add_atom_matcher() = matcher1;
+
+ AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+ int64_t matcher2Id = matcher2.id();
+ *config.add_atom_matcher() = matcher2;
+
+ AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
+ int64_t matcher3Id = matcher3.id();
+ *config.add_atom_matcher() = matcher3;
+
+ AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher();
+ int64_t matcher4Id = matcher4.id();
+ *config.add_atom_matcher() = matcher4;
+
+ // Add an event metric with multiple activations.
+ EventMetric event1 = createEventMetric("EVENT1", matcher1Id, nullopt);
+ int64_t event1Id = event1.id();
+ *config.add_event_metric() = event1;
+
+ int64_t matcher2TtlSec = 2, matcher3TtlSec = 3, matcher4TtlSec = 4;
+ MetricActivation metricActivation;
+ metricActivation.set_metric_id(event1Id);
+ EventActivation* activation = metricActivation.add_event_activation();
+ activation->set_atom_matcher_id(matcher2Id);
+ activation->set_ttl_seconds(matcher2TtlSec);
+ activation->set_activation_type(ACTIVATE_IMMEDIATELY);
+ activation->set_deactivation_atom_matcher_id(matcher1Id);
+ activation = metricActivation.add_event_activation();
+ activation->set_atom_matcher_id(matcher3Id);
+ activation->set_ttl_seconds(matcher3TtlSec);
+ activation->set_activation_type(ACTIVATE_ON_BOOT);
+ activation->set_deactivation_atom_matcher_id(matcher1Id);
+ activation = metricActivation.add_event_activation();
+ activation->set_atom_matcher_id(matcher4Id);
+ activation->set_ttl_seconds(matcher4TtlSec);
+ activation->set_activation_type(ACTIVATE_IMMEDIATELY);
+ activation->set_deactivation_atom_matcher_id(matcher2Id);
+ *config.add_metric_activation() = metricActivation;
+
+ EXPECT_TRUE(initConfig(config));
+
+ // Activate some of the event activations.
+ ASSERT_EQ(oldMetricProducers[0]->getMetricId(), event1Id);
+ int64_t matcher2StartNs = 12345;
+ oldMetricProducers[0]->activate(oldAtomMatchingTrackerMap[matcher2Id], matcher2StartNs);
+ int64_t matcher3StartNs = 23456;
+ oldMetricProducers[0]->activate(oldAtomMatchingTrackerMap[matcher3Id], matcher3StartNs);
+ EXPECT_TRUE(oldMetricProducers[0]->isActive());
+
+ // Map the matchers and predicates in reverse order to force the indices to change.
+ std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+ const int matcher4Index = 0;
+ newAtomMatchingTrackerMap[matcher4Id] = 0;
+ const int matcher3Index = 1;
+ newAtomMatchingTrackerMap[matcher3Id] = 1;
+ const int matcher2Index = 2;
+ newAtomMatchingTrackerMap[matcher2Id] = 2;
+ const int matcher1Index = 3;
+ newAtomMatchingTrackerMap[matcher1Id] = 3;
+ // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+ vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(4);
+ std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
+ newAtomMatchingTrackers.begin());
+ set<int64_t> replacedMatchers;
+
+ unordered_map<int64_t, int> newConditionTrackerMap;
+ vector<sp<ConditionTracker>> newConditionTrackers;
+ set<int64_t> replacedConditions;
+ vector<ConditionState> conditionCache;
+ unordered_map<int64_t, int> newMetricProducerMap;
+ vector<sp<MetricProducer>> newMetricProducers;
+ unordered_map<int, vector<int>> conditionToMetricMap;
+ unordered_map<int, vector<int>> trackerToMetricMap;
+ set<int64_t> noReportMetricIds;
+ unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+ vector<int> metricsWithActivation;
+ set<int64_t> replacedMetrics;
+ EXPECT_TRUE(updateMetrics(
+ key, config, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
+ newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions,
+ newConditionTrackers, conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{},
+ /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
+ newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, replacedMetrics));
+
+ // Verify event activation/deactivation maps.
+ ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 3);
+ EXPECT_THAT(activationAtomTrackerToMetricMap[matcher2Index], UnorderedElementsAre(0));
+ EXPECT_THAT(activationAtomTrackerToMetricMap[matcher3Index], UnorderedElementsAre(0));
+ EXPECT_THAT(activationAtomTrackerToMetricMap[matcher4Index], UnorderedElementsAre(0));
+ ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 2);
+ EXPECT_THAT(deactivationAtomTrackerToMetricMap[matcher1Index], UnorderedElementsAre(0, 0));
+ EXPECT_THAT(deactivationAtomTrackerToMetricMap[matcher2Index], UnorderedElementsAre(0));
+ ASSERT_EQ(metricsWithActivation.size(), 1);
+ EXPECT_THAT(metricsWithActivation, UnorderedElementsAre(0));
+
+ // Verify mEventActivation and mEventDeactivation map of the producer.
+ sp<MetricProducer> producer = newMetricProducers[0];
+ EXPECT_TRUE(producer->isActive());
+ ASSERT_EQ(producer->mEventActivationMap.size(), 3);
+ shared_ptr<Activation> matcher2Activation = producer->mEventActivationMap[matcher2Index];
+ EXPECT_EQ(matcher2Activation->ttl_ns, matcher2TtlSec * NS_PER_SEC);
+ EXPECT_EQ(matcher2Activation->activationType, ACTIVATE_IMMEDIATELY);
+ EXPECT_EQ(matcher2Activation->state, kActive);
+ EXPECT_EQ(matcher2Activation->start_ns, matcher2StartNs);
+ shared_ptr<Activation> matcher3Activation = producer->mEventActivationMap[matcher3Index];
+ EXPECT_EQ(matcher3Activation->ttl_ns, matcher3TtlSec * NS_PER_SEC);
+ EXPECT_EQ(matcher3Activation->activationType, ACTIVATE_ON_BOOT);
+ EXPECT_EQ(matcher3Activation->state, kActiveOnBoot);
+ shared_ptr<Activation> matcher4Activation = producer->mEventActivationMap[matcher4Index];
+ EXPECT_EQ(matcher4Activation->ttl_ns, matcher4TtlSec * NS_PER_SEC);
+ EXPECT_EQ(matcher4Activation->activationType, ACTIVATE_IMMEDIATELY);
+ EXPECT_EQ(matcher4Activation->state, kNotActive);
+
+ ASSERT_EQ(producer->mEventDeactivationMap.size(), 2);
+ EXPECT_THAT(producer->mEventDeactivationMap[matcher1Index],
+ UnorderedElementsAre(matcher2Activation, matcher3Activation));
+ EXPECT_THAT(producer->mEventDeactivationMap[matcher2Index],
+ UnorderedElementsAre(matcher4Activation));
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateMetricsMultipleTypes) {
+ StatsdConfig config;
+ // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig
+ AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+ int64_t matcher1Id = matcher1.id();
+ *config.add_atom_matcher() = matcher1;
+
+ AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+ int64_t matcher2Id = matcher2.id();
+ *config.add_atom_matcher() = matcher2;
+
+ AtomMatcher matcher3 = CreateTemperatureAtomMatcher();
+ int64_t matcher3Id = matcher3.id();
+ *config.add_atom_matcher() = matcher3;
+
+ Predicate predicate1 = CreateScreenIsOnPredicate();
+ int64_t predicate1Id = predicate1.id();
+ *config.add_predicate() = predicate1;
+
+ // Add a few count metrics.
+ // Will be preserved.
+ CountMetric countMetric = createCountMetric("COUNT1", matcher1Id, predicate1Id, {});
+ int64_t countMetricId = countMetric.id();
+ *config.add_count_metric() = countMetric;
+
+ // Will be replaced since matcher2 is replaced.
+ EventMetric eventMetric = createEventMetric("EVENT1", matcher2Id, nullopt);
+ int64_t eventMetricId = eventMetric.id();
+ *config.add_event_metric() = eventMetric;
+
+ // Will be replaced because the definition changes - a predicate is added.
+ GaugeMetric gaugeMetric = createGaugeMetric("GAUGE1", matcher3Id,
+ GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, nullopt);
+ int64_t gaugeMetricId = gaugeMetric.id();
+ *config.add_gauge_metric() = gaugeMetric;
+
+ // Preserved.
+ ValueMetric valueMetric = createValueMetric("VALUE1", matcher3, predicate1Id, {});
+ int64_t valueMetricId = valueMetric.id();
+ *config.add_value_metric() = valueMetric;
+
+ // Preserved.
+ DurationMetric durationMetric = createDurationMetric("DURATION1", predicate1Id, nullopt, {});
+ int64_t durationMetricId = durationMetric.id();
+ *config.add_duration_metric() = durationMetric;
+
+ EXPECT_TRUE(initConfig(config));
+
+ // Used later to ensure the condition wizard is replaced. Get it before doing the update.
+ sp<ConditionWizard> oldConditionWizard = oldMetricProducers[0]->mWizard;
+ EXPECT_EQ(oldConditionWizard->getStrongCount(), 6);
+
+ // Mark matcher 2 as replaced. Causes eventMetric to be replaced.
+ set<int64_t> replacedMatchers;
+ replacedMatchers.insert(matcher2Id);
+
+ // Add predicate1 as a predicate on gaugeMetric, causing it to be replaced.
+ gaugeMetric.set_condition(predicate1Id);
+
+ // Map the matchers and predicates in reverse order to force the indices to change.
+ std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+ const int matcher3Index = 0;
+ newAtomMatchingTrackerMap[matcher3Id] = 0;
+ const int matcher2Index = 1;
+ newAtomMatchingTrackerMap[matcher2Id] = 1;
+ const int matcher1Index = 2;
+ newAtomMatchingTrackerMap[matcher1Id] = 2;
+ // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+ vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(3);
+ std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
+ newAtomMatchingTrackers.begin());
+
+ std::unordered_map<int64_t, int> newConditionTrackerMap;
+ const int predicate1Index = 0;
+ newConditionTrackerMap[predicate1Id] = 0;
+ // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them.
+ vector<sp<ConditionTracker>> newConditionTrackers(1);
+ std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
+ newConditionTrackers.begin());
+ vector<ConditionState> conditionCache = {ConditionState::kUnknown};
+
+ // The order matters. we parse in the order of: count, duration, event, value, gauge.
+ StatsdConfig newConfig;
+ *newConfig.add_count_metric() = countMetric;
+ const int countMetricIndex = 0;
+ *newConfig.add_duration_metric() = durationMetric;
+ const int durationMetricIndex = 1;
+ *newConfig.add_event_metric() = eventMetric;
+ const int eventMetricIndex = 2;
+ *newConfig.add_value_metric() = valueMetric;
+ const int valueMetricIndex = 3;
+ *newConfig.add_gauge_metric() = gaugeMetric;
+ const int gaugeMetricIndex = 4;
+
+ // Add the predicate since duration metric needs it.
+ *newConfig.add_predicate() = predicate1;
+
+ // Output data structures to validate.
+ unordered_map<int64_t, int> newMetricProducerMap;
+ vector<sp<MetricProducer>> newMetricProducers;
+ unordered_map<int, vector<int>> conditionToMetricMap;
+ unordered_map<int, vector<int>> trackerToMetricMap;
+ set<int64_t> noReportMetricIds;
+ unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+ vector<int> metricsWithActivation;
+ set<int64_t> replacedMetrics;
+ EXPECT_TRUE(updateMetrics(
+ key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
+ newAtomMatchingTrackers, newConditionTrackerMap, /*replacedConditions=*/{},
+ newConditionTrackers, conditionCache, /*stateAtomIdMap*/ {}, /*allStateGroupMaps=*/{},
+ /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
+ newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, replacedMetrics));
+
+ unordered_map<int64_t, int> expectedMetricProducerMap = {
+ {countMetricId, countMetricIndex}, {durationMetricId, durationMetricIndex},
+ {eventMetricId, eventMetricIndex}, {valueMetricId, valueMetricIndex},
+ {gaugeMetricId, gaugeMetricIndex},
+ };
+ EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+
+ EXPECT_EQ(replacedMetrics, set<int64_t>({eventMetricId, gaugeMetricId}));
+
+ // Make sure preserved metrics are the same.
+ ASSERT_EQ(newMetricProducers.size(), 5);
+ EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(countMetricId)],
+ newMetricProducers[newMetricProducerMap.at(countMetricId)]);
+ EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(durationMetricId)],
+ newMetricProducers[newMetricProducerMap.at(durationMetricId)]);
+ EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(valueMetricId)],
+ newMetricProducers[newMetricProducerMap.at(valueMetricId)]);
+
+ // Make sure replaced metrics are different.
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(eventMetricId)],
+ newMetricProducers[newMetricProducerMap.at(eventMetricId)]);
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gaugeMetricId)],
+ newMetricProducers[newMetricProducerMap.at(gaugeMetricId)]);
+
+ // Verify the conditionToMetricMap.
+ ASSERT_EQ(conditionToMetricMap.size(), 1);
+ const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
+ EXPECT_THAT(condition1Metrics,
+ UnorderedElementsAre(countMetricIndex, gaugeMetricIndex, valueMetricIndex));
+
+ // Verify the trackerToMetricMap.
+ ASSERT_EQ(trackerToMetricMap.size(), 3);
+ const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
+ EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(countMetricIndex, durationMetricIndex));
+ const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index];
+ EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(eventMetricIndex, durationMetricIndex));
+ const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
+ EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(gaugeMetricIndex, valueMetricIndex));
+
+ // Verify event activation/deactivation maps.
+ ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
+ ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
+ ASSERT_EQ(metricsWithActivation.size(), 0);
+
+ // Verify tracker indices/ids/conditions are correct.
+ EXPECT_EQ(newMetricProducers[countMetricIndex]->getMetricId(), countMetricId);
+ EXPECT_EQ(newMetricProducers[countMetricIndex]->mConditionTrackerIndex, predicate1Index);
+ EXPECT_EQ(newMetricProducers[countMetricIndex]->mCondition, ConditionState::kUnknown);
+ EXPECT_EQ(newMetricProducers[durationMetricIndex]->getMetricId(), durationMetricId);
+ EXPECT_EQ(newMetricProducers[durationMetricIndex]->mConditionTrackerIndex, -1);
+ EXPECT_EQ(newMetricProducers[durationMetricIndex]->mCondition, ConditionState::kTrue);
+ EXPECT_EQ(newMetricProducers[eventMetricIndex]->getMetricId(), eventMetricId);
+ EXPECT_EQ(newMetricProducers[eventMetricIndex]->mConditionTrackerIndex, -1);
+ EXPECT_EQ(newMetricProducers[eventMetricIndex]->mCondition, ConditionState::kTrue);
+ EXPECT_EQ(newMetricProducers[gaugeMetricIndex]->getMetricId(), gaugeMetricId);
+ EXPECT_EQ(newMetricProducers[gaugeMetricIndex]->mConditionTrackerIndex, predicate1Index);
+ EXPECT_EQ(newMetricProducers[gaugeMetricIndex]->mCondition, ConditionState::kUnknown);
+
+ sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard;
+ EXPECT_NE(newConditionWizard, oldConditionWizard);
+ EXPECT_EQ(newConditionWizard->getStrongCount(), 6);
+ oldMetricProducers.clear();
+ // Only reference to the old wizard should be the one in the test.
+ EXPECT_EQ(oldConditionWizard->getStrongCount(), 1);
+}
+
+TEST_F(ConfigUpdateTest, TestAlertPreserve) {
+ StatsdConfig config;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ *config.add_count_metric() = createCountMetric("VALUE1", whatMatcher.id(), nullopt, {});
+
+ Alert alert = createAlert("Alert1", config.count_metric(0).id(), 1, 1);
+ *config.add_alert() = alert;
+ EXPECT_TRUE(initConfig(config));
+
+ UpdateStatus updateStatus = UPDATE_UNKNOWN;
+ EXPECT_TRUE(determineAlertUpdateStatus(alert, oldAlertTrackerMap, oldAnomalyTrackers,
+ /*replacedMetrics*/ {}, updateStatus));
+ EXPECT_EQ(updateStatus, UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestAlertMetricChanged) {
+ StatsdConfig config;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ CountMetric metric = createCountMetric("VALUE1", whatMatcher.id(), nullopt, {});
+ *config.add_count_metric() = metric;
+
+ Alert alert = createAlert("Alert1", config.count_metric(0).id(), 1, 1);
+ *config.add_alert() = alert;
+ EXPECT_TRUE(initConfig(config));
+
+ UpdateStatus updateStatus = UPDATE_UNKNOWN;
+ EXPECT_TRUE(determineAlertUpdateStatus(alert, oldAlertTrackerMap, oldAnomalyTrackers,
+ /*replacedMetrics*/ {metric.id()}, updateStatus));
+ EXPECT_EQ(updateStatus, UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestAlertDefinitionChanged) {
+ StatsdConfig config;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ *config.add_count_metric() = createCountMetric("VALUE1", whatMatcher.id(), nullopt, {});
+
+ Alert alert = createAlert("Alert1", config.count_metric(0).id(), 1, 1);
+ *config.add_alert() = alert;
+ EXPECT_TRUE(initConfig(config));
+
+ alert.set_num_buckets(2);
+
+ UpdateStatus updateStatus = UPDATE_UNKNOWN;
+ EXPECT_TRUE(determineAlertUpdateStatus(alert, oldAlertTrackerMap, oldAnomalyTrackers,
+ /*replacedMetrics*/ {}, updateStatus));
+ EXPECT_EQ(updateStatus, UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateAlerts) {
+ StatsdConfig config;
+ // Add atom matchers/predicates/metrics. These are mostly needed for initStatsdConfig
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+ *config.add_predicate() = CreateScreenIsOnPredicate();
+
+ CountMetric countMetric = createCountMetric("COUNT1", config.atom_matcher(0).id(), nullopt, {});
+ int64_t countMetricId = countMetric.id();
+ *config.add_count_metric() = countMetric;
+
+ DurationMetric durationMetric =
+ createDurationMetric("DURATION1", config.predicate(0).id(), nullopt, {});
+ int64_t durationMetricId = durationMetric.id();
+ *config.add_duration_metric() = durationMetric;
+
+ // Add alerts.
+ // Preserved.
+ Alert alert1 = createAlert("Alert1", durationMetricId, /*buckets*/ 1, /*triggerSum*/ 5000);
+ int64_t alert1Id = alert1.id();
+ *config.add_alert() = alert1;
+
+ // Replaced.
+ Alert alert2 = createAlert("Alert2", countMetricId, /*buckets*/ 1, /*triggerSum*/ 2);
+ int64_t alert2Id = alert2.id();
+ *config.add_alert() = alert2;
+
+ // Replaced.
+ Alert alert3 = createAlert("Alert3", durationMetricId, /*buckets*/ 3, /*triggerSum*/ 5000);
+ int64_t alert3Id = alert3.id();
+ *config.add_alert() = alert3;
+
+ // Add Subscriptions.
+ Subscription subscription1 = createSubscription("S1", Subscription::ALERT, alert1Id);
+ *config.add_subscription() = subscription1;
+ Subscription subscription2 = createSubscription("S2", Subscription::ALERT, alert1Id);
+ *config.add_subscription() = subscription2;
+ Subscription subscription3 = createSubscription("S3", Subscription::ALERT, alert2Id);
+ *config.add_subscription() = subscription3;
+
+ EXPECT_TRUE(initConfig(config));
+
+ // Add a duration tracker to the duration metric to ensure durationTrackers are updated
+ // with the proper anomalyTrackers.
+ unique_ptr<LogEvent> event = CreateScreenStateChangedEvent(
+ timeBaseNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+ oldMetricProducers[1]->onMatchedLogEvent(0, *event.get());
+
+ // Change the count metric. Causes alert2 to be replaced.
+ config.mutable_count_metric(0)->set_bucket(ONE_DAY);
+ // Change num buckets on alert3, causing replacement.
+ alert3.set_num_buckets(5);
+
+ // New alert.
+ Alert alert4 = createAlert("Alert4", durationMetricId, /*buckets*/ 3, /*triggerSum*/ 10000);
+ int64_t alert4Id = alert4.id();
+
+ // Move subscription2 to be on alert2 and make a new subscription.
+ subscription2.set_rule_id(alert2Id);
+ Subscription subscription4 = createSubscription("S4", Subscription::ALERT, alert2Id);
+
+ // Create the new config. Modify the old one to avoid adding the matchers/predicates.
+ // Add alerts in different order so the map is changed.
+ config.clear_alert();
+ *config.add_alert() = alert4;
+ const int alert4Index = 0;
+ *config.add_alert() = alert3;
+ const int alert3Index = 1;
+ *config.add_alert() = alert1;
+ const int alert1Index = 2;
+ *config.add_alert() = alert2;
+ const int alert2Index = 3;
+
+ // Subscription3 is removed.
+ config.clear_subscription();
+ *config.add_subscription() = subscription4;
+ *config.add_subscription() = subscription2;
+ *config.add_subscription() = subscription1;
+
+ // Output data structures from update metrics. Don't care about the outputs besides
+ // replacedMetrics, but need to do this so that the metrics clear their anomaly trackers.
+ unordered_map<int64_t, int> newMetricProducerMap;
+ vector<sp<MetricProducer>> newMetricProducers;
+ unordered_map<int, vector<int>> conditionToMetricMap;
+ unordered_map<int, vector<int>> trackerToMetricMap;
+ set<int64_t> noReportMetricIds;
+ unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+ vector<int> metricsWithActivation;
+ set<int64_t> replacedMetrics;
+ EXPECT_TRUE(updateMetrics(
+ key, config, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+ oldAtomMatchingTrackerMap, oldAtomMatchingTrackerMap, /*replacedMatchers*/ {},
+ oldAtomMatchingTrackers, oldConditionTrackerMap, /*replacedConditions=*/{},
+ oldConditionTrackers, {ConditionState::kUnknown}, /*stateAtomIdMap*/ {},
+ /*allStateGroupMaps=*/{},
+ /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
+ newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, replacedMetrics));
+
+ EXPECT_EQ(replacedMetrics, set<int64_t>({countMetricId}));
+
+ unordered_map<int64_t, int> newAlertTrackerMap;
+ vector<sp<AnomalyTracker>> newAnomalyTrackers;
+ EXPECT_TRUE(updateAlerts(config, newMetricProducerMap, replacedMetrics, oldAlertTrackerMap,
+ oldAnomalyTrackers, anomalyAlarmMonitor, newMetricProducers,
+ newAlertTrackerMap, newAnomalyTrackers));
+
+ unordered_map<int64_t, int> expectedAlertMap = {
+ {alert1Id, alert1Index},
+ {alert2Id, alert2Index},
+ {alert3Id, alert3Index},
+ {alert4Id, alert4Index},
+ };
+ EXPECT_THAT(newAlertTrackerMap, ContainerEq(expectedAlertMap));
+
+ // Make sure preserved alerts are the same.
+ ASSERT_EQ(newAnomalyTrackers.size(), 4);
+ EXPECT_EQ(oldAnomalyTrackers[oldAlertTrackerMap.at(alert1Id)],
+ newAnomalyTrackers[newAlertTrackerMap.at(alert1Id)]);
+
+ // Make sure replaced alerts are different.
+ EXPECT_NE(oldAnomalyTrackers[oldAlertTrackerMap.at(alert2Id)],
+ newAnomalyTrackers[newAlertTrackerMap.at(alert2Id)]);
+ EXPECT_NE(oldAnomalyTrackers[oldAlertTrackerMap.at(alert3Id)],
+ newAnomalyTrackers[newAlertTrackerMap.at(alert3Id)]);
+
+ // Verify the alerts have the correct anomaly trackers.
+ ASSERT_EQ(newMetricProducers.size(), 2);
+ EXPECT_THAT(newMetricProducers[0]->mAnomalyTrackers,
+ UnorderedElementsAre(newAnomalyTrackers[alert2Index]));
+ // For durationMetric, make sure the duration trackers get the updated anomalyTrackers.
+ DurationMetricProducer* durationProducer =
+ static_cast<DurationMetricProducer*>(newMetricProducers[1].get());
+ EXPECT_THAT(
+ durationProducer->mAnomalyTrackers,
+ UnorderedElementsAre(newAnomalyTrackers[alert1Index], newAnomalyTrackers[alert3Index],
+ newAnomalyTrackers[alert4Index]));
+ ASSERT_EQ(durationProducer->mCurrentSlicedDurationTrackerMap.size(), 1);
+ for (const auto& durationTrackerIt : durationProducer->mCurrentSlicedDurationTrackerMap) {
+ EXPECT_EQ(durationTrackerIt.second->mAnomalyTrackers, durationProducer->mAnomalyTrackers);
+ }
+
+ // Verify alerts have the correct subscriptions. Use subscription id as proxy for equivalency.
+ vector<int64_t> alert1Subscriptions;
+ for (const Subscription& subscription : newAnomalyTrackers[alert1Index]->mSubscriptions) {
+ alert1Subscriptions.push_back(subscription.id());
+ }
+ EXPECT_THAT(alert1Subscriptions, UnorderedElementsAre(subscription1.id()));
+ vector<int64_t> alert2Subscriptions;
+ for (const Subscription& subscription : newAnomalyTrackers[alert2Index]->mSubscriptions) {
+ alert2Subscriptions.push_back(subscription.id());
+ }
+ EXPECT_THAT(alert2Subscriptions, UnorderedElementsAre(subscription2.id(), subscription4.id()));
+ EXPECT_THAT(newAnomalyTrackers[alert3Index]->mSubscriptions, IsEmpty());
+ EXPECT_THAT(newAnomalyTrackers[alert4Index]->mSubscriptions, IsEmpty());
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateAlarms) {
+ StatsdConfig config;
+ // Add alarms.
+ Alarm alarm1 = createAlarm("Alarm1", /*offset*/ 1 * MS_PER_SEC, /*period*/ 50 * MS_PER_SEC);
+ int64_t alarm1Id = alarm1.id();
+ *config.add_alarm() = alarm1;
+
+ Alarm alarm2 = createAlarm("Alarm2", /*offset*/ 1 * MS_PER_SEC, /*period*/ 2000 * MS_PER_SEC);
+ int64_t alarm2Id = alarm2.id();
+ *config.add_alarm() = alarm2;
+
+ Alarm alarm3 = createAlarm("Alarm3", /*offset*/ 10 * MS_PER_SEC, /*period*/ 5000 * MS_PER_SEC);
+ int64_t alarm3Id = alarm3.id();
+ *config.add_alarm() = alarm3;
+
+ // Add Subscriptions.
+ Subscription subscription1 = createSubscription("S1", Subscription::ALARM, alarm1Id);
+ *config.add_subscription() = subscription1;
+ Subscription subscription2 = createSubscription("S2", Subscription::ALARM, alarm1Id);
+ *config.add_subscription() = subscription2;
+ Subscription subscription3 = createSubscription("S3", Subscription::ALARM, alarm2Id);
+ *config.add_subscription() = subscription3;
+
+ EXPECT_TRUE(initConfig(config));
+
+ ASSERT_EQ(oldAlarmTrackers.size(), 3);
+ // Config is created at statsd start time, so just add the offsets.
+ EXPECT_EQ(oldAlarmTrackers[0]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 1);
+ EXPECT_EQ(oldAlarmTrackers[1]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 1);
+ EXPECT_EQ(oldAlarmTrackers[2]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 10);
+
+ // Change alarm2/alarm3.
+ config.mutable_alarm(1)->set_offset_millis(5 * MS_PER_SEC);
+ config.mutable_alarm(2)->set_period_millis(10000 * MS_PER_SEC);
+
+ // Move subscription2 to be on alarm2 and make a new subscription.
+ config.mutable_subscription(1)->set_rule_id(alarm2Id);
+ Subscription subscription4 = createSubscription("S4", Subscription::ALARM, alarm1Id);
+ *config.add_subscription() = subscription4;
+
+ // Update time is 2 seconds after the base time.
+ int64_t currentTimeNs = timeBaseNs + 2 * NS_PER_SEC;
+ vector<sp<AlarmTracker>> newAlarmTrackers;
+ EXPECT_TRUE(initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs,
+ newAlarmTrackers));
+
+ ASSERT_EQ(newAlarmTrackers.size(), 3);
+ // Config is updated 2 seconds after statsd start
+ // The offset has passed for alarm1, but not for alarms 2/3.
+ EXPECT_EQ(newAlarmTrackers[0]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 1 + 50);
+ EXPECT_EQ(newAlarmTrackers[1]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 5);
+ EXPECT_EQ(newAlarmTrackers[2]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 10);
+
+ // Verify alarms have the correct subscriptions. Use subscription id as proxy for equivalency.
+ vector<int64_t> alarm1Subscriptions;
+ for (const Subscription& subscription : newAlarmTrackers[0]->mSubscriptions) {
+ alarm1Subscriptions.push_back(subscription.id());
+ }
+ EXPECT_THAT(alarm1Subscriptions, UnorderedElementsAre(subscription1.id(), subscription4.id()));
+ vector<int64_t> alarm2Subscriptions;
+ for (const Subscription& subscription : newAlarmTrackers[1]->mSubscriptions) {
+ alarm2Subscriptions.push_back(subscription.id());
+ }
+ EXPECT_THAT(alarm2Subscriptions, UnorderedElementsAre(subscription2.id(), subscription3.id()));
+ EXPECT_THAT(newAlarmTrackers[2]->mSubscriptions, IsEmpty());
+
+ // Verify the alarm monitor is updated accordingly once the old alarms are removed.
+ // Alarm2 fires the earliest.
+ oldAlarmTrackers.clear();
+ EXPECT_EQ(periodicAlarmMonitor->getRegisteredAlarmTimeSec(), timeBaseNs / NS_PER_SEC + 5);
+
+ // Do another update 60 seconds after config creation time, after the offsets of each alarm.
+ currentTimeNs = timeBaseNs + 60 * NS_PER_SEC;
+ newAlarmTrackers.clear();
+ EXPECT_TRUE(initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs,
+ newAlarmTrackers));
+
+ ASSERT_EQ(newAlarmTrackers.size(), 3);
+ // Config is updated one minute after statsd start.
+ // Two periods have passed for alarm 1, one has passed for alarms2/3.
+ EXPECT_EQ(newAlarmTrackers[0]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 1 + 2 * 50);
+ EXPECT_EQ(newAlarmTrackers[1]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 5 + 2000);
+ EXPECT_EQ(newAlarmTrackers[2]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 10 + 10000);
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/bin/tests/metrics/parsing_utils/metrics_manager_util_test.cpp b/bin/tests/metrics/parsing_utils/metrics_manager_util_test.cpp
new file mode 100644
index 0000000..00690b5
--- /dev/null
+++ b/bin/tests/metrics/parsing_utils/metrics_manager_util_test.cpp
@@ -0,0 +1,928 @@
+// Copyright (C) 2020 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 "src/metrics/parsing_utils/metrics_manager_util.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <private/android_filesystem_config.h>
+#include <stdio.h>
+
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+#include "packages/modules/StatsD/bin/src/statsd_config.pb.h"
+#include "src/condition/ConditionTracker.h"
+#include "src/matchers/AtomMatchingTracker.h"
+#include "src/metrics/CountMetricProducer.h"
+#include "src/metrics/DurationMetricProducer.h"
+#include "src/metrics/GaugeMetricProducer.h"
+#include "src/metrics/MetricProducer.h"
+#include "src/metrics/ValueMetricProducer.h"
+#include "src/state/StateManager.h"
+#include "tests/metrics/metrics_test_helper.h"
+#include "tests/statsd_test_util.h"
+
+using namespace testing;
+using android::sp;
+using android::os::statsd::Predicate;
+using std::map;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+namespace {
+const ConfigKey kConfigKey(0, 12345);
+const long kAlertId = 3;
+
+const long timeBaseSec = 1000;
+
+StatsdConfig buildGoodConfig() {
+ StatsdConfig config;
+ config.set_id(12345);
+
+ AtomMatcher* eventMatcher = config.add_atom_matcher();
+ eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
+
+ SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+ simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+ simpleAtomMatcher->add_field_value_matcher()->set_field(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
+ 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+ eventMatcher = config.add_atom_matcher();
+ eventMatcher->set_id(StringToId("SCREEN_IS_OFF"));
+
+ simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+ simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+ simpleAtomMatcher->add_field_value_matcher()->set_field(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
+
+ eventMatcher = config.add_atom_matcher();
+ eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
+
+ AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
+ combination->set_operation(LogicalOperation::OR);
+ combination->add_matcher(StringToId("SCREEN_IS_ON"));
+ combination->add_matcher(StringToId("SCREEN_IS_OFF"));
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_id(3);
+ metric->set_what(StringToId("SCREEN_IS_ON"));
+ metric->set_bucket(ONE_MINUTE);
+ metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/);
+ metric->mutable_dimensions_in_what()->add_child()->set_field(1);
+
+ config.add_no_report_metric(3);
+
+ auto alert = config.add_alert();
+ alert->set_id(kAlertId);
+ alert->set_metric_id(3);
+ alert->set_num_buckets(10);
+ alert->set_refractory_period_secs(100);
+ alert->set_trigger_if_sum_gt(100);
+ return config;
+}
+
+StatsdConfig buildCircleMatchers() {
+ StatsdConfig config;
+ config.set_id(12345);
+
+ AtomMatcher* eventMatcher = config.add_atom_matcher();
+ eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
+
+ SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+ simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+ simpleAtomMatcher->add_field_value_matcher()->set_field(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
+ 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+ eventMatcher = config.add_atom_matcher();
+ eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
+
+ AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
+ combination->set_operation(LogicalOperation::OR);
+ combination->add_matcher(StringToId("SCREEN_IS_ON"));
+ // Circle dependency
+ combination->add_matcher(StringToId("SCREEN_ON_OR_OFF"));
+
+ return config;
+}
+
+StatsdConfig buildAlertWithUnknownMetric() {
+ StatsdConfig config;
+ config.set_id(12345);
+
+ AtomMatcher* eventMatcher = config.add_atom_matcher();
+ eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_id(3);
+ metric->set_what(StringToId("SCREEN_IS_ON"));
+ metric->set_bucket(ONE_MINUTE);
+ metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/);
+ metric->mutable_dimensions_in_what()->add_child()->set_field(1);
+
+ auto alert = config.add_alert();
+ alert->set_id(3);
+ alert->set_metric_id(2);
+ alert->set_num_buckets(10);
+ alert->set_refractory_period_secs(100);
+ alert->set_trigger_if_sum_gt(100);
+ return config;
+}
+
+StatsdConfig buildMissingMatchers() {
+ StatsdConfig config;
+ config.set_id(12345);
+
+ AtomMatcher* eventMatcher = config.add_atom_matcher();
+ eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
+
+ SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+ simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+ simpleAtomMatcher->add_field_value_matcher()->set_field(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
+ 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+ eventMatcher = config.add_atom_matcher();
+ eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
+
+ AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
+ combination->set_operation(LogicalOperation::OR);
+ combination->add_matcher(StringToId("SCREEN_IS_ON"));
+ // undefined matcher
+ combination->add_matcher(StringToId("ABC"));
+
+ return config;
+}
+
+StatsdConfig buildMissingPredicate() {
+ StatsdConfig config;
+ config.set_id(12345);
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_id(3);
+ metric->set_what(StringToId("SCREEN_EVENT"));
+ metric->set_bucket(ONE_MINUTE);
+ metric->set_condition(StringToId("SOME_CONDITION"));
+
+ AtomMatcher* eventMatcher = config.add_atom_matcher();
+ eventMatcher->set_id(StringToId("SCREEN_EVENT"));
+
+ SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+ simpleAtomMatcher->set_atom_id(2);
+
+ return config;
+}
+
+StatsdConfig buildDimensionMetricsWithMultiTags() {
+ StatsdConfig config;
+ config.set_id(12345);
+
+ AtomMatcher* eventMatcher = config.add_atom_matcher();
+ eventMatcher->set_id(StringToId("BATTERY_VERY_LOW"));
+ SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+ simpleAtomMatcher->set_atom_id(2);
+
+ eventMatcher = config.add_atom_matcher();
+ eventMatcher->set_id(StringToId("BATTERY_VERY_VERY_LOW"));
+ simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+ simpleAtomMatcher->set_atom_id(3);
+
+ eventMatcher = config.add_atom_matcher();
+ eventMatcher->set_id(StringToId("BATTERY_LOW"));
+
+ AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
+ combination->set_operation(LogicalOperation::OR);
+ combination->add_matcher(StringToId("BATTERY_VERY_LOW"));
+ combination->add_matcher(StringToId("BATTERY_VERY_VERY_LOW"));
+
+ // Count process state changes, slice by uid, while SCREEN_IS_OFF
+ CountMetric* metric = config.add_count_metric();
+ metric->set_id(3);
+ metric->set_what(StringToId("BATTERY_LOW"));
+ metric->set_bucket(ONE_MINUTE);
+ // This case is interesting. We want to dimension across two atoms.
+ metric->mutable_dimensions_in_what()->add_child()->set_field(1);
+
+ auto alert = config.add_alert();
+ alert->set_id(kAlertId);
+ alert->set_metric_id(3);
+ alert->set_num_buckets(10);
+ alert->set_refractory_period_secs(100);
+ alert->set_trigger_if_sum_gt(100);
+ return config;
+}
+
+StatsdConfig buildCirclePredicates() {
+ StatsdConfig config;
+ config.set_id(12345);
+
+ AtomMatcher* eventMatcher = config.add_atom_matcher();
+ eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
+
+ SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+ simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+ simpleAtomMatcher->add_field_value_matcher()->set_field(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
+ 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+ eventMatcher = config.add_atom_matcher();
+ eventMatcher->set_id(StringToId("SCREEN_IS_OFF"));
+
+ simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+ simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+ simpleAtomMatcher->add_field_value_matcher()->set_field(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
+
+ auto condition = config.add_predicate();
+ condition->set_id(StringToId("SCREEN_IS_ON"));
+ SimplePredicate* simplePredicate = condition->mutable_simple_predicate();
+ simplePredicate->set_start(StringToId("SCREEN_IS_ON"));
+ simplePredicate->set_stop(StringToId("SCREEN_IS_OFF"));
+
+ condition = config.add_predicate();
+ condition->set_id(StringToId("SCREEN_IS_EITHER_ON_OFF"));
+
+ Predicate_Combination* combination = condition->mutable_combination();
+ combination->set_operation(LogicalOperation::OR);
+ combination->add_predicate(StringToId("SCREEN_IS_ON"));
+ combination->add_predicate(StringToId("SCREEN_IS_EITHER_ON_OFF"));
+
+ return config;
+}
+
+StatsdConfig buildConfigWithDifferentPredicates() {
+ StatsdConfig config;
+ config.set_id(12345);
+
+ auto pulledAtomMatcher =
+ CreateSimpleAtomMatcher("SUBSYSTEM_SLEEP", util::SUBSYSTEM_SLEEP_STATE);
+ *config.add_atom_matcher() = pulledAtomMatcher;
+ auto screenOnAtomMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = screenOnAtomMatcher;
+ auto screenOffAtomMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = screenOffAtomMatcher;
+ auto batteryNoneAtomMatcher = CreateBatteryStateNoneMatcher();
+ *config.add_atom_matcher() = batteryNoneAtomMatcher;
+ auto batteryUsbAtomMatcher = CreateBatteryStateUsbMatcher();
+ *config.add_atom_matcher() = batteryUsbAtomMatcher;
+
+ // Simple condition with InitialValue set to default (unknown).
+ auto screenOnUnknownPredicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = screenOnUnknownPredicate;
+
+ // Simple condition with InitialValue set to false.
+ auto screenOnFalsePredicate = config.add_predicate();
+ screenOnFalsePredicate->set_id(StringToId("ScreenIsOnInitialFalse"));
+ SimplePredicate* simpleScreenOnFalsePredicate =
+ screenOnFalsePredicate->mutable_simple_predicate();
+ simpleScreenOnFalsePredicate->set_start(screenOnAtomMatcher.id());
+ simpleScreenOnFalsePredicate->set_stop(screenOffAtomMatcher.id());
+ simpleScreenOnFalsePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE);
+
+ // Simple condition with InitialValue set to false.
+ auto onBatteryFalsePredicate = config.add_predicate();
+ onBatteryFalsePredicate->set_id(StringToId("OnBatteryInitialFalse"));
+ SimplePredicate* simpleOnBatteryFalsePredicate =
+ onBatteryFalsePredicate->mutable_simple_predicate();
+ simpleOnBatteryFalsePredicate->set_start(batteryNoneAtomMatcher.id());
+ simpleOnBatteryFalsePredicate->set_stop(batteryUsbAtomMatcher.id());
+ simpleOnBatteryFalsePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE);
+
+ // Combination condition with both simple condition InitialValues set to false.
+ auto screenOnFalseOnBatteryFalsePredicate = config.add_predicate();
+ screenOnFalseOnBatteryFalsePredicate->set_id(StringToId("ScreenOnFalseOnBatteryFalse"));
+ screenOnFalseOnBatteryFalsePredicate->mutable_combination()->set_operation(
+ LogicalOperation::AND);
+ addPredicateToPredicateCombination(*screenOnFalsePredicate,
+ screenOnFalseOnBatteryFalsePredicate);
+ addPredicateToPredicateCombination(*onBatteryFalsePredicate,
+ screenOnFalseOnBatteryFalsePredicate);
+
+ // Combination condition with one simple condition InitialValue set to unknown and one set to
+ // false.
+ auto screenOnUnknownOnBatteryFalsePredicate = config.add_predicate();
+ screenOnUnknownOnBatteryFalsePredicate->set_id(StringToId("ScreenOnUnknowneOnBatteryFalse"));
+ screenOnUnknownOnBatteryFalsePredicate->mutable_combination()->set_operation(
+ LogicalOperation::AND);
+ addPredicateToPredicateCombination(screenOnUnknownPredicate,
+ screenOnUnknownOnBatteryFalsePredicate);
+ addPredicateToPredicateCombination(*onBatteryFalsePredicate,
+ screenOnUnknownOnBatteryFalsePredicate);
+
+ // Simple condition metric with initial value false.
+ ValueMetric* metric1 = config.add_value_metric();
+ metric1->set_id(StringToId("ValueSubsystemSleepWhileScreenOnInitialFalse"));
+ metric1->set_what(pulledAtomMatcher.id());
+ *metric1->mutable_value_field() =
+ CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
+ metric1->set_bucket(FIVE_MINUTES);
+ metric1->set_condition(screenOnFalsePredicate->id());
+
+ // Simple condition metric with initial value unknown.
+ ValueMetric* metric2 = config.add_value_metric();
+ metric2->set_id(StringToId("ValueSubsystemSleepWhileScreenOnInitialUnknown"));
+ metric2->set_what(pulledAtomMatcher.id());
+ *metric2->mutable_value_field() =
+ CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
+ metric2->set_bucket(FIVE_MINUTES);
+ metric2->set_condition(screenOnUnknownPredicate.id());
+
+ // Combination condition metric with initial values false and false.
+ ValueMetric* metric3 = config.add_value_metric();
+ metric3->set_id(StringToId("ValueSubsystemSleepWhileScreenOnFalseDeviceUnpluggedFalse"));
+ metric3->set_what(pulledAtomMatcher.id());
+ *metric3->mutable_value_field() =
+ CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
+ metric3->set_bucket(FIVE_MINUTES);
+ metric3->set_condition(screenOnFalseOnBatteryFalsePredicate->id());
+
+ // Combination condition metric with initial values unknown and false.
+ ValueMetric* metric4 = config.add_value_metric();
+ metric4->set_id(StringToId("ValueSubsystemSleepWhileScreenOnUnknownDeviceUnpluggedFalse"));
+ metric4->set_what(pulledAtomMatcher.id());
+ *metric4->mutable_value_field() =
+ CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
+ metric4->set_bucket(FIVE_MINUTES);
+ metric4->set_condition(screenOnUnknownOnBatteryFalsePredicate->id());
+
+ return config;
+}
+} // anonymous namespace
+
+TEST(MetricsManagerTest, TestInitialConditions) {
+ sp<UidMap> uidMap = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
+ StatsdConfig config = buildConfigWithDifferentPredicates();
+ set<int> allTagIds;
+ vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
+ unordered_map<int64_t, int> atomMatchingTrackerMap;
+ vector<sp<ConditionTracker>> allConditionTrackers;
+ unordered_map<int64_t, int> conditionTrackerMap;
+ vector<sp<MetricProducer>> allMetricProducers;
+ unordered_map<int64_t, int> metricProducerMap;
+ std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+ std::vector<sp<AlarmTracker>> allAlarmTrackers;
+ unordered_map<int, std::vector<int>> conditionToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToConditionMap;
+ unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+ unordered_map<int64_t, int> alertTrackerMap;
+ vector<int> metricsWithActivation;
+ map<int64_t, uint64_t> stateProtoHashes;
+ std::set<int64_t> noReportMetricIds;
+
+ EXPECT_TRUE(initStatsdConfig(
+ kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+ timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+ allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
+ allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+ trackerToConditionMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+ stateProtoHashes, noReportMetricIds));
+ ASSERT_EQ(4u, allMetricProducers.size());
+ ASSERT_EQ(5u, allConditionTrackers.size());
+
+ ConditionKey queryKey;
+ vector<ConditionState> conditionCache(5, ConditionState::kNotEvaluated);
+
+ allConditionTrackers[3]->isConditionMet(queryKey, allConditionTrackers, false, conditionCache);
+ allConditionTrackers[4]->isConditionMet(queryKey, allConditionTrackers, false, conditionCache);
+ EXPECT_EQ(ConditionState::kUnknown, conditionCache[0]);
+ EXPECT_EQ(ConditionState::kFalse, conditionCache[1]);
+ EXPECT_EQ(ConditionState::kFalse, conditionCache[2]);
+ EXPECT_EQ(ConditionState::kFalse, conditionCache[3]);
+ EXPECT_EQ(ConditionState::kUnknown, conditionCache[4]);
+
+ EXPECT_EQ(ConditionState::kFalse, allMetricProducers[0]->mCondition);
+ EXPECT_EQ(ConditionState::kUnknown, allMetricProducers[1]->mCondition);
+ EXPECT_EQ(ConditionState::kFalse, allMetricProducers[2]->mCondition);
+ EXPECT_EQ(ConditionState::kUnknown, allMetricProducers[3]->mCondition);
+}
+
+TEST(MetricsManagerTest, TestGoodConfig) {
+ sp<UidMap> uidMap = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
+ StatsdConfig config = buildGoodConfig();
+ set<int> allTagIds;
+ vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
+ unordered_map<int64_t, int> atomMatchingTrackerMap;
+ vector<sp<ConditionTracker>> allConditionTrackers;
+ unordered_map<int64_t, int> conditionTrackerMap;
+ vector<sp<MetricProducer>> allMetricProducers;
+ unordered_map<int64_t, int> metricProducerMap;
+ std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+ std::vector<sp<AlarmTracker>> allAlarmTrackers;
+ unordered_map<int, std::vector<int>> conditionToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToConditionMap;
+ unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+ unordered_map<int64_t, int> alertTrackerMap;
+ vector<int> metricsWithActivation;
+ map<int64_t, uint64_t> stateProtoHashes;
+ std::set<int64_t> noReportMetricIds;
+
+ EXPECT_TRUE(initStatsdConfig(
+ kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+ timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+ allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
+ allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+ trackerToConditionMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+ stateProtoHashes, noReportMetricIds));
+ ASSERT_EQ(1u, allMetricProducers.size());
+ EXPECT_THAT(metricProducerMap, UnorderedElementsAre(Pair(config.count_metric(0).id(), 0)));
+ ASSERT_EQ(1u, allAnomalyTrackers.size());
+ ASSERT_EQ(1u, noReportMetricIds.size());
+ ASSERT_EQ(1u, alertTrackerMap.size());
+ EXPECT_NE(alertTrackerMap.find(kAlertId), alertTrackerMap.end());
+ EXPECT_EQ(alertTrackerMap.find(kAlertId)->second, 0);
+}
+
+TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) {
+ sp<UidMap> uidMap = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
+ StatsdConfig config = buildDimensionMetricsWithMultiTags();
+ set<int> allTagIds;
+ vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
+ unordered_map<int64_t, int> atomMatchingTrackerMap;
+ vector<sp<ConditionTracker>> allConditionTrackers;
+ unordered_map<int64_t, int> conditionTrackerMap;
+ vector<sp<MetricProducer>> allMetricProducers;
+ unordered_map<int64_t, int> metricProducerMap;
+ std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+ std::vector<sp<AlarmTracker>> allAlarmTrackers;
+ unordered_map<int, std::vector<int>> conditionToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToConditionMap;
+ unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+ unordered_map<int64_t, int> alertTrackerMap;
+ vector<int> metricsWithActivation;
+ map<int64_t, uint64_t> stateProtoHashes;
+ std::set<int64_t> noReportMetricIds;
+
+ EXPECT_FALSE(initStatsdConfig(
+ kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+ timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+ allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
+ allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+ trackerToConditionMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+ stateProtoHashes, noReportMetricIds));
+}
+
+TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
+ sp<UidMap> uidMap = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
+ StatsdConfig config = buildCircleMatchers();
+ set<int> allTagIds;
+ vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
+ unordered_map<int64_t, int> atomMatchingTrackerMap;
+ vector<sp<ConditionTracker>> allConditionTrackers;
+ unordered_map<int64_t, int> conditionTrackerMap;
+ vector<sp<MetricProducer>> allMetricProducers;
+ unordered_map<int64_t, int> metricProducerMap;
+ std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+ std::vector<sp<AlarmTracker>> allAlarmTrackers;
+ unordered_map<int, std::vector<int>> conditionToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToConditionMap;
+ unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+ unordered_map<int64_t, int> alertTrackerMap;
+ vector<int> metricsWithActivation;
+ map<int64_t, uint64_t> stateProtoHashes;
+ std::set<int64_t> noReportMetricIds;
+
+ EXPECT_FALSE(initStatsdConfig(
+ kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+ timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+ allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
+ allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+ trackerToConditionMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+ stateProtoHashes, noReportMetricIds));
+}
+
+TEST(MetricsManagerTest, TestMissingMatchers) {
+ sp<UidMap> uidMap = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
+ StatsdConfig config = buildMissingMatchers();
+ set<int> allTagIds;
+ vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
+ unordered_map<int64_t, int> atomMatchingTrackerMap;
+ vector<sp<ConditionTracker>> allConditionTrackers;
+ unordered_map<int64_t, int> conditionTrackerMap;
+ vector<sp<MetricProducer>> allMetricProducers;
+ unordered_map<int64_t, int> metricProducerMap;
+ std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+ std::vector<sp<AlarmTracker>> allAlarmTrackers;
+ unordered_map<int, std::vector<int>> conditionToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToConditionMap;
+ unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+ unordered_map<int64_t, int> alertTrackerMap;
+ vector<int> metricsWithActivation;
+ map<int64_t, uint64_t> stateProtoHashes;
+ std::set<int64_t> noReportMetricIds;
+ EXPECT_FALSE(initStatsdConfig(
+ kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+ timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+ allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
+ allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+ trackerToConditionMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+ stateProtoHashes, noReportMetricIds));
+}
+
+TEST(MetricsManagerTest, TestMissingPredicate) {
+ sp<UidMap> uidMap = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
+ StatsdConfig config = buildMissingPredicate();
+ set<int> allTagIds;
+ vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
+ unordered_map<int64_t, int> atomMatchingTrackerMap;
+ vector<sp<ConditionTracker>> allConditionTrackers;
+ unordered_map<int64_t, int> conditionTrackerMap;
+ vector<sp<MetricProducer>> allMetricProducers;
+ unordered_map<int64_t, int> metricProducerMap;
+ std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+ std::vector<sp<AlarmTracker>> allAlarmTrackers;
+ unordered_map<int, std::vector<int>> conditionToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToConditionMap;
+ unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+ unordered_map<int64_t, int> alertTrackerMap;
+ vector<int> metricsWithActivation;
+ map<int64_t, uint64_t> stateProtoHashes;
+ std::set<int64_t> noReportMetricIds;
+ EXPECT_FALSE(initStatsdConfig(
+ kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+ timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+ allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
+ allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+ trackerToConditionMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+ stateProtoHashes, noReportMetricIds));
+}
+
+TEST(MetricsManagerTest, TestCirclePredicateDependency) {
+ sp<UidMap> uidMap = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
+ StatsdConfig config = buildCirclePredicates();
+ set<int> allTagIds;
+ vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
+ unordered_map<int64_t, int> atomMatchingTrackerMap;
+ vector<sp<ConditionTracker>> allConditionTrackers;
+ unordered_map<int64_t, int> conditionTrackerMap;
+ vector<sp<MetricProducer>> allMetricProducers;
+ unordered_map<int64_t, int> metricProducerMap;
+ std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+ std::vector<sp<AlarmTracker>> allAlarmTrackers;
+ unordered_map<int, std::vector<int>> conditionToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToConditionMap;
+ unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+ unordered_map<int64_t, int> alertTrackerMap;
+ vector<int> metricsWithActivation;
+ map<int64_t, uint64_t> stateProtoHashes;
+ std::set<int64_t> noReportMetricIds;
+
+ EXPECT_FALSE(initStatsdConfig(
+ kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+ timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+ allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
+ allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+ trackerToConditionMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+ stateProtoHashes, noReportMetricIds));
+}
+
+TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
+ sp<UidMap> uidMap = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
+ StatsdConfig config = buildAlertWithUnknownMetric();
+ set<int> allTagIds;
+ vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
+ unordered_map<int64_t, int> atomMatchingTrackerMap;
+ vector<sp<ConditionTracker>> allConditionTrackers;
+ unordered_map<int64_t, int> conditionTrackerMap;
+ vector<sp<MetricProducer>> allMetricProducers;
+ unordered_map<int64_t, int> metricProducerMap;
+ std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+ std::vector<sp<AlarmTracker>> allAlarmTrackers;
+ unordered_map<int, std::vector<int>> conditionToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToConditionMap;
+ unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+ unordered_map<int64_t, int> alertTrackerMap;
+ vector<int> metricsWithActivation;
+ map<int64_t, uint64_t> stateProtoHashes;
+ std::set<int64_t> noReportMetricIds;
+
+ EXPECT_FALSE(initStatsdConfig(
+ kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+ timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+ allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
+ allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+ trackerToConditionMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+ stateProtoHashes, noReportMetricIds));
+}
+
+TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerInvalidMatcher) {
+ sp<UidMap> uidMap = new UidMap();
+ AtomMatcher matcher;
+ // Matcher has no contents_case (simple/combination), so it is invalid.
+ matcher.set_id(21);
+ EXPECT_EQ(createAtomMatchingTracker(matcher, 0, uidMap), nullptr);
+}
+
+TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerSimple) {
+ int index = 1;
+ int64_t id = 123;
+ sp<UidMap> uidMap = new UidMap();
+ AtomMatcher matcher;
+ matcher.set_id(id);
+ SimpleAtomMatcher* simpleAtomMatcher = matcher.mutable_simple_atom_matcher();
+ simpleAtomMatcher->set_atom_id(util::SCREEN_STATE_CHANGED);
+ simpleAtomMatcher->add_field_value_matcher()->set_field(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+
+ sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(matcher, index, uidMap);
+ EXPECT_NE(tracker, nullptr);
+
+ EXPECT_TRUE(tracker->mInitialized);
+ EXPECT_EQ(tracker->getId(), id);
+ EXPECT_EQ(tracker->mIndex, index);
+ const set<int>& atomIds = tracker->getAtomIds();
+ ASSERT_EQ(atomIds.size(), 1);
+ EXPECT_EQ(atomIds.count(util::SCREEN_STATE_CHANGED), 1);
+}
+
+TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerCombination) {
+ int index = 1;
+ int64_t id = 123;
+ sp<UidMap> uidMap = new UidMap();
+ AtomMatcher matcher;
+ matcher.set_id(id);
+ AtomMatcher_Combination* combination = matcher.mutable_combination();
+ combination->set_operation(LogicalOperation::OR);
+ combination->add_matcher(123);
+ combination->add_matcher(223);
+
+ sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(matcher, index, uidMap);
+ EXPECT_NE(tracker, nullptr);
+
+ // Combination matchers need to be initialized first.
+ EXPECT_FALSE(tracker->mInitialized);
+ EXPECT_EQ(tracker->getId(), id);
+ EXPECT_EQ(tracker->mIndex, index);
+ const set<int>& atomIds = tracker->getAtomIds();
+ ASSERT_EQ(atomIds.size(), 0);
+}
+
+TEST(MetricsManagerTest, TestCreateConditionTrackerInvalid) {
+ const ConfigKey key(123, 456);
+ // Predicate has no contents_case (simple/combination), so it is invalid.
+ Predicate predicate;
+ predicate.set_id(21);
+ unordered_map<int64_t, int> atomTrackerMap;
+ EXPECT_EQ(createConditionTracker(key, predicate, 0, atomTrackerMap), nullptr);
+}
+
+TEST(MetricsManagerTest, TestCreateConditionTrackerSimple) {
+ int index = 1;
+ int64_t id = 987;
+ const ConfigKey key(123, 456);
+
+ int startMatcherIndex = 2, stopMatcherIndex = 0, stopAllMatcherIndex = 1;
+ int64_t startMatcherId = 246, stopMatcherId = 153, stopAllMatcherId = 975;
+
+ Predicate predicate;
+ predicate.set_id(id);
+ SimplePredicate* simplePredicate = predicate.mutable_simple_predicate();
+ simplePredicate->set_start(startMatcherId);
+ simplePredicate->set_stop(stopMatcherId);
+ simplePredicate->set_stop_all(stopAllMatcherId);
+
+ unordered_map<int64_t, int> atomTrackerMap;
+ atomTrackerMap[startMatcherId] = startMatcherIndex;
+ atomTrackerMap[stopMatcherId] = stopMatcherIndex;
+ atomTrackerMap[stopAllMatcherId] = stopAllMatcherIndex;
+
+ sp<ConditionTracker> tracker = createConditionTracker(key, predicate, index, atomTrackerMap);
+ EXPECT_EQ(tracker->getConditionId(), id);
+ EXPECT_EQ(tracker->isSliced(), false);
+ EXPECT_TRUE(tracker->IsSimpleCondition());
+ const set<int>& interestedMatchers = tracker->getAtomMatchingTrackerIndex();
+ ASSERT_EQ(interestedMatchers.size(), 3);
+ ASSERT_EQ(interestedMatchers.count(startMatcherIndex), 1);
+ ASSERT_EQ(interestedMatchers.count(stopMatcherIndex), 1);
+ ASSERT_EQ(interestedMatchers.count(stopAllMatcherIndex), 1);
+}
+
+TEST(MetricsManagerTest, TestCreateConditionTrackerCombination) {
+ int index = 1;
+ int64_t id = 987;
+ const ConfigKey key(123, 456);
+
+ Predicate predicate;
+ predicate.set_id(id);
+ Predicate_Combination* combinationPredicate = predicate.mutable_combination();
+ combinationPredicate->set_operation(LogicalOperation::AND);
+ combinationPredicate->add_predicate(888);
+ combinationPredicate->add_predicate(777);
+
+ // Combination conditions must be initialized to set most state.
+ unordered_map<int64_t, int> atomTrackerMap;
+ sp<ConditionTracker> tracker = createConditionTracker(key, predicate, index, atomTrackerMap);
+ EXPECT_EQ(tracker->getConditionId(), id);
+ EXPECT_FALSE(tracker->IsSimpleCondition());
+}
+
+TEST(MetricsManagerTest, TestCreateAnomalyTrackerInvalidMetric) {
+ Alert alert;
+ alert.set_id(123);
+ alert.set_metric_id(1);
+ alert.set_trigger_if_sum_gt(1);
+ alert.set_num_buckets(1);
+
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ vector<sp<MetricProducer>> metricProducers;
+ // Pass in empty metric producers, causing an error.
+ EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, {}, metricProducers), nullopt);
+}
+
+TEST(MetricsManagerTest, TestCreateAnomalyTrackerNoThreshold) {
+ int64_t metricId = 1;
+ Alert alert;
+ alert.set_id(123);
+ alert.set_metric_id(metricId);
+ alert.set_num_buckets(1);
+
+ CountMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ vector<sp<MetricProducer>> metricProducers({new CountMetricProducer(
+ kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, 0x0123456789, 0, 0)});
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, {{1, 0}}, metricProducers), nullopt);
+}
+
+TEST(MetricsManagerTest, TestCreateAnomalyTrackerMissingBuckets) {
+ int64_t metricId = 1;
+ Alert alert;
+ alert.set_id(123);
+ alert.set_metric_id(metricId);
+ alert.set_trigger_if_sum_gt(1);
+
+ CountMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ vector<sp<MetricProducer>> metricProducers({new CountMetricProducer(
+ kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, 0x0123456789, 0, 0)});
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, {{1, 0}}, metricProducers), nullopt);
+}
+
+TEST(MetricsManagerTest, TestCreateAnomalyTrackerGood) {
+ int64_t metricId = 1;
+ Alert alert;
+ alert.set_id(123);
+ alert.set_metric_id(metricId);
+ alert.set_trigger_if_sum_gt(1);
+ alert.set_num_buckets(1);
+
+ CountMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ vector<sp<MetricProducer>> metricProducers({new CountMetricProducer(
+ kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, 0x0123456789, 0, 0)});
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ EXPECT_NE(createAnomalyTracker(alert, anomalyAlarmMonitor, {{1, 0}}, metricProducers), nullopt);
+}
+
+TEST(MetricsManagerTest, TestCreateAnomalyTrackerDurationTooLong) {
+ int64_t metricId = 1;
+ Alert alert;
+ alert.set_id(123);
+ alert.set_metric_id(metricId);
+ // Impossible for alert to fire since the time is bigger than bucketSize * numBuckets
+ alert.set_trigger_if_sum_gt(MillisToNano(TimeUnitToBucketSizeInMillis(ONE_MINUTE)) + 1);
+ alert.set_num_buckets(1);
+
+ DurationMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
+ FieldMatcher dimensions;
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ vector<sp<MetricProducer>> metricProducers({new DurationMetricProducer(
+ kConfigKey, metric, -1 /*no condition*/, {}, -1 /* what index not needed*/,
+ 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
+ wizard, 0x0123456789, dimensions, 0, 0)});
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, {{1, 0}}, metricProducers), nullopt);
+}
+
+TEST(MetricsManagerTest, TestCreateDurationProducerDimensionsInWhatInvalid) {
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT");
+ *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
+ *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
+ *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
+ *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
+
+ Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+ // The predicate is dimensioning by first attribution node by uid.
+ FieldMatcher dimensions =
+ CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions;
+ *config.add_predicate() = holdingWakelockPredicate;
+
+ DurationMetric* durationMetric = config.add_duration_metric();
+ durationMetric->set_id(StringToId("WakelockDuration"));
+ durationMetric->set_what(holdingWakelockPredicate.id());
+ durationMetric->set_aggregation_type(DurationMetric::SUM);
+ // The metric is dimensioning by first attribution node by uid AND tag.
+ // Invalid since the predicate only dimensions by uid.
+ *durationMetric->mutable_dimensions_in_what() = CreateAttributionUidAndOtherDimensions(
+ util::WAKELOCK_STATE_CHANGED, {Position::FIRST}, {3 /* tag */});
+ durationMetric->set_bucket(FIVE_MINUTES);
+
+ ConfigKey key(123, 987);
+ uint64_t timeNs = 456;
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
+ sp<UidMap> uidMap;
+ sp<MetricsManager> metricsManager =
+ new MetricsManager(key, config, timeNs, timeNs, uidMap, pullerManager,
+ anomalyAlarmMonitor, periodicAlarmMonitor);
+ EXPECT_FALSE(metricsManager->isConfigValid());
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/bin/tests/shell/ShellSubscriber_test.cpp b/bin/tests/shell/ShellSubscriber_test.cpp
index 5d10bf6..7b131fc 100644
--- a/bin/tests/shell/ShellSubscriber_test.cpp
+++ b/bin/tests/shell/ShellSubscriber_test.cpp
@@ -190,9 +190,9 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
const vector<int32_t> uids = {AID_SYSTEM};
- EXPECT_CALL(*pullerManager, Pull(10016, uids, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(10016, uids, _, _))
.WillRepeatedly(Invoke([](int tagId, const vector<int32_t>&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(makeCpuActiveTimeAtom(/*uid=*/kUid1, /*timeMillis=*/kCpuTime1));
data->push_back(makeCpuActiveTimeAtom(/*uid=*/kUid2, /*timeMillis=*/kCpuTime2));
diff --git a/bin/tests/statsd_test_util.cpp b/bin/tests/statsd_test_util.cpp
index cee8372..153b696 100644
--- a/bin/tests/statsd_test_util.cpp
+++ b/bin/tests/statsd_test_util.cpp
@@ -15,6 +15,8 @@
#include "statsd_test_util.h"
#include <aidl/android/util/StatsEventParcel.h>
+
+#include "matchers/SimpleAtomMatchingTracker.h"
#include "stats_event.h"
using aidl::android::util::StatsEventParcel;
@@ -996,6 +998,20 @@
return static_cast<int64_t>(std::hash<std::string>()(str));
}
+sp<EventMatcherWizard> createEventMatcherWizard(
+ int tagId, int matcherIndex, const vector<FieldValueMatcher>& fieldValueMatchers) {
+ sp<UidMap> uidMap = new UidMap();
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ for (const FieldValueMatcher& fvm : fieldValueMatchers) {
+ *atomMatcher.add_field_value_matcher() = fvm;
+ }
+ uint64_t matcherHash = 0x12345678;
+ int64_t matcherId = 678;
+ return new EventMatcherWizard({new SimpleAtomMatchingTracker(
+ matcherId, matcherIndex, matcherHash, atomMatcher, uidMap)});
+}
+
void ValidateWakelockAttributionUidAndTagDimension(const DimensionsValue& value, const int atomId,
const int uid, const string& tag) {
EXPECT_EQ(value.field(), atomId);
@@ -1075,6 +1091,21 @@
.value_tuple().dimensions_value(1).value_str(), tag);
}
+void ValidateStateValue(const google::protobuf::RepeatedPtrField<StateValue>& stateValues,
+ int atomId, int64_t value) {
+ ASSERT_EQ(stateValues.size(), 1);
+ ASSERT_EQ(stateValues[0].atom_id(), atomId);
+ switch (stateValues[0].contents_case()) {
+ case StateValue::ContentsCase::kValue:
+ EXPECT_EQ(stateValues[0].value(), (int32_t)value);
+ break;
+ case StateValue::ContentsCase::kGroupId:
+ EXPECT_EQ(stateValues[0].group_id(), value);
+ break;
+ default:
+ FAIL() << "State value should have either a value or a group id";
+ }
+}
bool EqualsTo(const DimensionsValue& s1, const DimensionsValue& s2) {
if (s1.field() != s2.field()) {
return false;
diff --git a/bin/tests/statsd_test_util.h b/bin/tests/statsd_test_util.h
index eb2f294..dc9b285 100644
--- a/bin/tests/statsd_test_util.h
+++ b/bin/tests/statsd_test_util.h
@@ -25,6 +25,7 @@
#include "src/StatsLogProcessor.h"
#include "src/hash.h"
#include "src/logd/LogEvent.h"
+#include "src/matchers/EventMatcherWizard.h"
#include "src/packages/UidMap.h"
#include "src/stats_log_util.h"
#include "stats_event.h"
@@ -335,6 +336,9 @@
int64_t StringToId(const string& str);
+sp<EventMatcherWizard> createEventMatcherWizard(
+ int tagId, int matcherIndex, const std::vector<FieldValueMatcher>& fieldValueMatchers = {});
+
void ValidateWakelockAttributionUidAndTagDimension(const DimensionsValue& value, const int atomId,
const int uid, const string& tag);
void ValidateUidDimension(const DimensionsValue& value, int node_idx, int atomId, int uid);
@@ -343,6 +347,8 @@
const DimensionsValue& value, int atomId, int uid, const std::string& tag);
void ValidateAttributionUidAndTagDimension(
const DimensionsValue& value, int node_idx, int atomId, int uid, const std::string& tag);
+void ValidateStateValue(const google::protobuf::RepeatedPtrField<StateValue>& stateValues,
+ int atomId, int64_t value);
struct DimensionsPair {
DimensionsPair(DimensionsValue m1, google::protobuf::RepeatedPtrField<StateValue> m2)
diff --git a/framework/java/android/app/StatsManager.java b/framework/java/android/app/StatsManager.java
index a7d2057..41803cf 100644
--- a/framework/java/android/app/StatsManager.java
+++ b/framework/java/android/app/StatsManager.java
@@ -547,7 +547,7 @@
@Override
public void onPullAtom(int atomTag, IPullAtomResultReceiver resultReceiver) {
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
mExecutor.execute(() -> {
List<StatsEvent> data = new ArrayList<>();
diff --git a/framework/test/Android.bp b/framework/test/Android.bp
index b113d59..5cc5647 100644
--- a/framework/test/Android.bp
+++ b/framework/test/Android.bp
@@ -14,12 +14,8 @@
android_test {
name: "FrameworkStatsdTest",
- platform_apis: true,
- srcs: [
- // TODO(b/147705194): Use framework-statsd as a lib dependency instead.
- ":framework-statsd-sources",
- "**/*.java",
- ],
+ sdk_version: "module_current",
+ srcs: [ "**/*.java" ],
manifest: "AndroidManifest.xml",
static_libs: [
"androidx.test.rules",
@@ -28,9 +24,10 @@
libs: [
"android.test.runner.stubs",
"android.test.base.stubs",
+ "framework-statsd.impl",
],
test_suites: [
"device-tests",
"mts",
],
-}
\ No newline at end of file
+}
diff --git a/lib/libstatspull/Android.bp b/lib/libstatspull/Android.bp
index 6d6b466..54ef7a8 100644
--- a/lib/libstatspull/Android.bp
+++ b/lib/libstatspull/Android.bp
@@ -30,18 +30,39 @@
shared_libs: [
"libbinder_ndk",
"liblog",
- "libstatssocket",
],
static_libs: [
"libutils",
"statsd-aidl-ndk_platform",
],
+ target: {
+ android: {
+ shared_libs: ["libstatssocket"],
+ },
+ host: {
+ static_libs: ["libstatssocket"],
+ },
+ },
}
-cc_library_shared {
+cc_library {
name: "libstatspull",
defaults: [
- "libstatspull_defaults"
+ "libstatspull_defaults",
+ "libbinder_ndk_host_user",
],
+ host_supported: true,
+ target: {
+ android: {
+ static: {
+ enabled: false,
+ },
+ },
+ host: {
+ shared: {
+ enabled: false,
+ },
+ },
+ },
// enumerate stable entry points for APEX use
stubs: {
symbol_file: "libstatspull.map.txt",
@@ -68,6 +89,9 @@
defaults: [
"libstatspull_defaults",
],
+ cflags: [
+ "-DLIB_STATS_PULL_TESTS_FLAG",
+ ],
visibility: [
"//frameworks/base/apex/statsd/tests/libstatspull",
"//packages/modules/StatsD/apex/tests/libstatspull",
diff --git a/lib/libstatspull/stats_pull_atom_callback.cpp b/lib/libstatspull/stats_pull_atom_callback.cpp
index 478cae7..2d924a5 100644
--- a/lib/libstatspull/stats_pull_atom_callback.cpp
+++ b/lib/libstatspull/stats_pull_atom_callback.cpp
@@ -120,6 +120,9 @@
// Convert stats_events into StatsEventParcels.
std::vector<StatsEventParcel> parcels;
+
+ // Resolves fuzz build failure in b/161575591.
+#if defined(__ANDROID_APEX__) || defined(LIB_STATS_PULL_TESTS_FLAG)
for (int i = 0; i < statsEventList.data.size(); i++) {
size_t size;
uint8_t* buffer = AStatsEvent_getBuffer(statsEventList.data[i], &size);
@@ -130,6 +133,7 @@
p.buffer.assign(buffer, buffer + size);
parcels.push_back(std::move(p));
}
+#endif
Status status = resultReceiver->pullFinished(atomTag, success, parcels);
if (!status.isOk()) {
diff --git a/lib/libstatssocket/stats_event.c b/lib/libstatssocket/stats_event.c
index f3e8087..dcd34aa 100644
--- a/lib/libstatssocket/stats_event.c
+++ b/lib/libstatssocket/stats_event.c
@@ -232,14 +232,19 @@
void AStatsEvent_writeByteArray(AStatsEvent* event, const uint8_t* buf, size_t numBytes) {
start_field(event, BYTE_ARRAY_TYPE);
+ if (buf == NULL) {
+ numBytes = 0;
+ }
append_int32(event, numBytes);
- append_byte_array(event, buf, numBytes);
+ if (numBytes > 0) {
+ append_byte_array(event, buf, numBytes);
+ }
}
// Value is assumed to be encoded using UTF8
void AStatsEvent_writeString(AStatsEvent* event, const char* value) {
start_field(event, STRING_TYPE);
- append_string(event, value);
+ append_string(event, value == NULL ? "" : value);
}
// Tags are assumed to be encoded using UTF8
@@ -255,7 +260,7 @@
for (uint8_t i = 0; i < numNodes; i++) {
append_int32(event, uids[i]);
- append_string(event, tags[i]);
+ append_string(event, tags[i] == NULL ? "" : tags[i]);
}
}
diff --git a/lib/libstatssocket/tests/stats_event_test.cpp b/lib/libstatssocket/tests/stats_event_test.cpp
index 9a1fac8..2f9ccdc 100644
--- a/lib/libstatssocket/tests/stats_event_test.cpp
+++ b/lib/libstatssocket/tests/stats_event_test.cpp
@@ -183,6 +183,31 @@
AStatsEvent_release(event);
}
+TEST(StatsEventTest, TestNullString) {
+ uint32_t atomId = 100;
+ char* str = nullptr;
+
+ int64_t startTime = android::elapsedRealtimeNano();
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, atomId);
+ AStatsEvent_writeString(event, str);
+ AStatsEvent_build(event);
+ int64_t endTime = android::elapsedRealtimeNano();
+
+ size_t bufferSize;
+ uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
+ uint8_t* bufferEnd = buffer + bufferSize;
+
+ checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
+
+ checkTypeHeader(&buffer, STRING_TYPE);
+ checkString(&buffer, "");
+
+ EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
+ EXPECT_EQ(AStatsEvent_getErrors(event), 0);
+ AStatsEvent_release(event);
+}
+
TEST(StatsEventTest, TestByteArrays) {
uint32_t atomId = 100;
vector<uint8_t> message = {'b', 'y', 't', '\0', 'e', 's'};
@@ -208,6 +233,32 @@
AStatsEvent_release(event);
}
+TEST(StatsEventTest, TestNullByteArrays) {
+ uint32_t atomId = 100;
+ uint8_t* buf = nullptr;
+ vector<uint8_t> message;
+
+ int64_t startTime = android::elapsedRealtimeNano();
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, atomId);
+ AStatsEvent_writeByteArray(event, buf, 2);
+ AStatsEvent_build(event);
+ int64_t endTime = android::elapsedRealtimeNano();
+
+ size_t bufferSize;
+ uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
+ uint8_t* bufferEnd = buffer + bufferSize;
+
+ checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
+
+ checkTypeHeader(&buffer, BYTE_ARRAY_TYPE);
+ checkByteArray(&buffer, message);
+
+ EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
+ EXPECT_EQ(AStatsEvent_getErrors(event), 0);
+ AStatsEvent_release(event);
+}
+
TEST(StatsEventTest, TestAttributionChains) {
uint32_t atomId = 100;
@@ -217,8 +268,13 @@
const char* cTags[numNodes];
for (int i = 0; i < (int)numNodes; i++) {
uids[i] = i;
- tags.push_back("test" + std::to_string(i));
- cTags[i] = tags[i].c_str();
+ if (0 == i) {
+ tags.push_back("");
+ cTags[i] = nullptr;
+ } else {
+ tags.push_back("test" + std::to_string(i));
+ cTags[i] = tags[i].c_str();
+ }
}
int64_t startTime = android::elapsedRealtimeNano();
diff --git a/service/java/com/android/server/stats/StatsCompanionService.java b/service/java/com/android/server/stats/StatsCompanionService.java
index b5e7224..fbda86f 100644
--- a/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/service/java/com/android/server/stats/StatsCompanionService.java
@@ -54,6 +54,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -194,40 +195,38 @@
int numRecords = 0;
// Add in all the apps for every user/profile.
for (UserHandle userHandle : users) {
- List<PackageInfo> pi =
- pm.getInstalledPackagesAsUser(PackageManager.MATCH_UNINSTALLED_PACKAGES
- | PackageManager.MATCH_ANY_USER
- | PackageManager.MATCH_APEX,
- userHandle.getIdentifier());
- for (int j = 0; j < pi.size(); j++) {
- if (pi.get(j).applicationInfo != null) {
+ List<PackageInfo> packagesPlusApex = getAllPackagesWithApex(pm, userHandle);
+ for (int j = 0; j < packagesPlusApex.size(); j++) {
+ if (packagesPlusApex.get(j).applicationInfo != null) {
String installer;
try {
- installer = pm.getInstallerPackageName(pi.get(j).packageName);
+ installer = pm.getInstallerPackageName(
+ packagesPlusApex.get(j).packageName);
} catch (IllegalArgumentException e) {
installer = "";
}
long applicationInfoToken =
output.start(ProtoOutputStream.FIELD_TYPE_MESSAGE
| ProtoOutputStream.FIELD_COUNT_REPEATED
- | APPLICATION_INFO_FIELD_ID);
+ | APPLICATION_INFO_FIELD_ID);
output.write(ProtoOutputStream.FIELD_TYPE_INT32
- | ProtoOutputStream.FIELD_COUNT_SINGLE | UID_FIELD_ID,
- pi.get(j).applicationInfo.uid);
+ | ProtoOutputStream.FIELD_COUNT_SINGLE | UID_FIELD_ID,
+ packagesPlusApex.get(j).applicationInfo.uid);
output.write(ProtoOutputStream.FIELD_TYPE_INT64
- | ProtoOutputStream.FIELD_COUNT_SINGLE
- | VERSION_FIELD_ID, pi.get(j).getLongVersionCode());
+ | ProtoOutputStream.FIELD_COUNT_SINGLE
+ | VERSION_FIELD_ID,
+ packagesPlusApex.get(j).getLongVersionCode());
+ output.write(ProtoOutputStream.FIELD_TYPE_STRING
+ | ProtoOutputStream.FIELD_COUNT_SINGLE
+ | VERSION_STRING_FIELD_ID,
+ packagesPlusApex.get(j).versionName);
output.write(ProtoOutputStream.FIELD_TYPE_STRING
| ProtoOutputStream.FIELD_COUNT_SINGLE
- | VERSION_STRING_FIELD_ID,
- pi.get(j).versionName);
+ | PACKAGE_NAME_FIELD_ID, packagesPlusApex.get(j).packageName);
output.write(ProtoOutputStream.FIELD_TYPE_STRING
- | ProtoOutputStream.FIELD_COUNT_SINGLE
- | PACKAGE_NAME_FIELD_ID, pi.get(j).packageName);
- output.write(ProtoOutputStream.FIELD_TYPE_STRING
- | ProtoOutputStream.FIELD_COUNT_SINGLE
+ | ProtoOutputStream.FIELD_COUNT_SINGLE
| INSTALLER_FIELD_ID,
- installer == null ? "" : installer);
+ installer == null ? "" : installer);
numRecords++;
output.end(applicationInfoToken);
}
@@ -245,6 +244,26 @@
});
}
+ private static List<PackageInfo> getAllPackagesWithApex(PackageManager pm,
+ UserHandle userHandle) {
+ // We want all the uninstalled packages because uninstalled package uids can still be logged
+ // to statsd.
+ List<PackageInfo> allPackages = new ArrayList<>(
+ pm.getInstalledPackagesAsUser(PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_ANY_USER,
+ userHandle.getIdentifier()));
+ // We make a second query to package manager for the apex modules because package manager
+ // returns both installed and uninstalled apexes with
+ // PackageManager.MATCH_UNINSTALLED_PACKAGES flag. We only want active apexes because
+ // inactive apexes can conflict with active ones.
+ for (PackageInfo packageInfo : pm.getInstalledPackages(PackageManager.MATCH_APEX)) {
+ if (packageInfo.isApex) {
+ allPackages.add(packageInfo);
+ }
+ }
+ return allPackages;
+ }
+
private static class WakelockThread extends Thread {
private final PowerManager.WakeLock mWl;
private final Runnable mRunnable;
diff --git a/service/java/com/android/server/stats/StatsManagerService.java b/service/java/com/android/server/stats/StatsManagerService.java
index 97846f2..1e3846b 100644
--- a/service/java/com/android/server/stats/StatsManagerService.java
+++ b/service/java/com/android/server/stats/StatsManagerService.java
@@ -27,6 +27,7 @@
import android.os.IPullAtomCallback;
import android.os.IStatsManagerService;
import android.os.IStatsd;
+import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.util.ArrayMap;
@@ -412,8 +413,13 @@
@Override
public byte[] getData(long key, String packageName) throws IllegalStateException {
enforceDumpAndUsageStatsPermission(packageName);
+ PowerManager powerManager = (PowerManager)
+ mContext.getSystemService(Context.POWER_SERVICE);
+ PowerManager.WakeLock wl = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ /*tag=*/ StatsManagerService.class.getCanonicalName());
int callingUid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
+ wl.acquire();
try {
IStatsd statsd = waitForStatsd();
if (statsd != null) {
@@ -423,6 +429,7 @@
Log.e(TAG, "Failed to getData with statsd");
throw new IllegalStateException(e.getMessage(), e);
} finally {
+ wl.release();
Binder.restoreCallingIdentity(token);
}
throw new IllegalStateException("Failed to connect to statsd to getData");
diff --git a/tests/Android.bp b/tests/Android.bp
index dccd5be..386606b 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -39,7 +39,5 @@
data: [
"**/*.pbtxt",
":CtsStatsdApp",
- ":CtsStatsdEmptyApp",
- ":CtsStatsdEmptySplitApp",
],
}
diff --git a/tests/TEST_MAPPING b/tests/TEST_MAPPING
new file mode 100644
index 0000000..f48ff4b
--- /dev/null
+++ b/tests/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit" : [
+ {
+ "name" : "CtsStatsdHostTestCases"
+ }
+ ]
+}
diff --git a/tests/apps/emptyapp/Android.bp b/tests/apps/emptyapp/Android.bp
deleted file mode 100644
index d01d14d..0000000
--- a/tests/apps/emptyapp/Android.bp
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (C) 2020 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.
-
-android_test_helper_app {
- name: "CtsStatsdEmptyApp",
- defaults: ["cts_defaults"],
- sdk_version: "current",
- v4_signature: true,
-}
-
-android_test_helper_app {
- name: "CtsStatsdEmptySplitApp",
- defaults: ["cts_defaults"],
- sdk_version: "current",
- v4_signature: true,
- package_splits: ["pl"],
-}
\ No newline at end of file
diff --git a/tests/apps/emptyapp/AndroidManifest.xml b/tests/apps/emptyapp/AndroidManifest.xml
deleted file mode 100644
index f40d070..0000000
--- a/tests/apps/emptyapp/AndroidManifest.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2020 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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.device.statsd.emptyapp">
- <application android:hasCode="false" android:label="Empty Test App" />
-</manifest>
-
diff --git a/tests/apps/statsdapp/AndroidManifest.xml b/tests/apps/statsdapp/AndroidManifest.xml
index 2b479bd..875488b 100644
--- a/tests/apps/statsdapp/AndroidManifest.xml
+++ b/tests/apps/statsdapp/AndroidManifest.xml
@@ -15,9 +15,9 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.cts.device.statsd"
- android:versionCode="10" >
- <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+ package="com.android.server.cts.device.statsd"
+ android:versionCode="10">
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
@@ -27,86 +27,91 @@
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
- <uses-permission android:name="android.permission.CONFIGURE_DISPLAY_BRIGHTNESS" />
- <uses-permission android:name="android.permission.DUMP" /> <!-- must be granted via pm grant -->
- <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.READ_SYNC_STATS" />
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
- <uses-permission android:name="android.permission.VIBRATE" />
- <uses-permission android:name="android.permission.WAKE_LOCK" />
- <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
+ <uses-permission android:name="android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"/>
+ <uses-permission android:name="android.permission.DUMP"/> <!-- must be granted via pm grant -->
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.READ_SYNC_STATS"/>
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
+ <uses-permission android:name="android.permission.VIBRATE"/>
+ <uses-permission android:name="android.permission.WAKE_LOCK"/>
+ <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
<application android:label="@string/app_name">
- <uses-library android:name="android.test.runner" />
- <uses-library android:name="org.apache.http.legacy" android:required="false" />
+ <uses-library android:name="android.test.runner"/>
+ <uses-library android:name="org.apache.http.legacy"
+ android:required="false"/>
- <service android:name=".StatsdCtsBackgroundService" android:exported="true" />
- <activity android:name=".StatsdCtsForegroundActivity" android:exported="true" />
+ <service android:name=".StatsdCtsBackgroundService"
+ android:exported="true"/>
+ <activity android:name=".StatsdCtsForegroundActivity"
+ android:exported="true"/>
<service android:name=".StatsdCtsForegroundService"
- android:foregroundServiceType="camera" android:exported="true" />
+ android:foregroundServiceType="camera"
+ android:exported="true"/>
- <activity
- android:name=".VideoPlayerActivity"
- android:label="@string/app_name"
- android:resizeableActivity="true"
- android:supportsPictureInPicture="true"
- android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
- android:launchMode="singleTop" >
+ <activity android:name=".VideoPlayerActivity"
+ android:label="@string/app_name"
+ android:resizeableActivity="true"
+ android:supportsPictureInPicture="true"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
+ android:launchMode="singleTop"
+ android:exported="true">
<intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
- <activity android:name=".DaveyActivity" android:exported="true" />
- <activity android:name=".HiddenApiUsedActivity" android:exported="true" />
- <activity
- android:name=".ANRActivity"
- android:label="ANR Test Activity"
- android:launchMode="singleInstance"
- android:process=":ANRProcess"
- android:exported="true"
- />
+ <activity android:name=".DaveyActivity"
+ android:exported="true"/>
+ <activity android:name=".ANRActivity"
+ android:label="ANR Test Activity"
+ android:launchMode="singleInstance"
+ android:process=":ANRProcess"
+ android:exported="true"/>
<service android:name=".StatsdAuthenticator"
- android:exported="false">
+ android:exported="false">
<intent-filter>
- <action android:name="android.accounts.AccountAuthenticator" />
+ <action android:name="android.accounts.AccountAuthenticator"/>
</intent-filter>
<meta-data android:name="android.accounts.AccountAuthenticator"
- android:resource="@xml/authenticator" />
+ android:resource="@xml/authenticator"/>
</service>
<service android:name="StatsdSyncService"
- android:exported="false" >
+ android:exported="false">
<intent-filter>
- <action android:name="android.content.SyncAdapter" />
+ <action android:name="android.content.SyncAdapter"/>
</intent-filter>
<meta-data android:name="android.content.SyncAdapter"
- android:resource="@xml/syncadapter" />
+ android:resource="@xml/syncadapter"/>
</service>
<provider android:name=".StatsdProvider"
- android:authorities="com.android.server.cts.device.statsd.provider" />
+ android:authorities="com.android.server.cts.device.statsd.provider"/>
<service android:name=".StatsdJobService"
- android:permission="android.permission.BIND_JOB_SERVICE" />
+ android:permission="android.permission.BIND_JOB_SERVICE"/>
<service android:name=".DummyCallscreeningService"
- android:permission="android.permission.BIND_SCREENING_SERVICE">
+ android:permission="android.permission.BIND_SCREENING_SERVICE"
+ android:exported="true">
<intent-filter>
- <action android:name="android.telecom.CallScreeningService" />
+ <action android:name="android.telecom.CallScreeningService"/>
</intent-filter>
</service>
- <service android:name=".IsolatedProcessService" android:isolatedProcess="true" />
+ <service android:name=".IsolatedProcessService"
+ android:isolatedProcess="true"/>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.cts.device.statsd"
- android:label="CTS tests of android.os.statsd stats collection">
+ android:targetPackage="com.android.server.cts.device.statsd"
+ android:label="CTS tests of android.os.statsd stats collection">
<meta-data android:name="listener"
- android:value="com.android.cts.runner.CtsTestRunListener" />
- </instrumentation>/>
+ android:value="com.android.cts.runner.CtsTestRunListener"/>
+ </instrumentation>
</manifest>
diff --git a/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java b/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
index 4eb659c..a53064b 100644
--- a/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
+++ b/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
@@ -17,7 +17,6 @@
package com.android.server.cts.device.statsd;
import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
-
import static com.google.common.truth.Truth.assertWithMessage;
import android.accounts.Account;
@@ -68,22 +67,17 @@
import android.os.SystemClock;
import android.os.VibrationEffect;
import android.os.Vibrator;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.StatsEvent;
import android.util.StatsLog;
-
import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
-
import com.android.compatibility.common.util.ShellIdentityUtils;
-import com.android.utils.blob.DummyBlobData;
-
+import com.android.utils.blob.FakeBlobData;
import com.google.common.io.BaseEncoding;
-
-import org.junit.Test;
-
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
@@ -93,6 +87,7 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
+import org.junit.Test;
public class AtomTests {
private static final String TAG = AtomTests.class.getSimpleName();
@@ -205,6 +200,7 @@
APP_OPS_ENUM_MAP.put(AppOpsManager.OPSTR_PHONE_CALL_CAMERA, 101);
APP_OPS_ENUM_MAP.put(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD, 102);
APP_OPS_ENUM_MAP.put(AppOpsManager.OPSTR_MANAGE_ONGOING_CALLS, 103);
+ APP_OPS_ENUM_MAP.put(AppOpsManager.OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER, 105);
}
@Test
@@ -262,23 +258,38 @@
int uid = Process.myUid();
int whatAtomId = 9_999;
+ // Get the current setting for bluetooth background scanning.
+ // Set to 0 if the setting is not found or an error occurs.
+ int initialBleScanGlobalSetting = Settings.Global.getInt(
+ InstrumentationRegistry.getTargetContext().getContentResolver(),
+ Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 0);
+
+ // Turn off bluetooth background scanning.
+ Settings.Global.putInt(InstrumentationRegistry.getTargetContext().getContentResolver(),
+ Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 0);
+
// Change state to State.ON.
bleScanner.startScan(null, scanSettings, scanCallback);
- sleep(500);
+ sleep(6_000);
writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
+
bluetoothAdapter.disable();
- sleep(1500);
+ sleep(6_000);
// Trigger State.RESET so that new state is State.OFF.
if (!bluetoothAdapter.enable()) {
Log.e(TAG, "Could not enable bluetooth to trigger state reset");
return;
}
- sleep(3_000); // Wait for Bluetooth to fully turn on.
+ sleep(6_000); // Wait for Bluetooth to fully turn on.
writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
+
+ // Set bluetooth background scanning to original setting.
+ Settings.Global.putInt(InstrumentationRegistry.getTargetContext().getContentResolver(),
+ Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, initialBleScanGlobalSetting);
});
}
@@ -938,20 +949,6 @@
}
}
- @Test
- public void testIsolatedProcessService() throws Exception {
- Context context = InstrumentationRegistry.getContext();
- int uid = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0).uid;
-
- // Start the isolated service, which logs an AppBreadcrumbReported atom, and then exit
- // shortly afterwards.
- Intent intent = new Intent(context, IsolatedProcessService.class);
- context.startService(intent);
- sleep(500);
- context.stopService(intent);
- }
-
-
// Constants for testBlobStore
private static final long BLOB_COMMIT_CALLBACK_TIMEOUT_SEC = 5;
private static final long BLOB_EXPIRY_DURATION_MS = 24 * 60 * 60 * 1000;
@@ -968,7 +965,7 @@
BlobStoreManager bsm = context.getSystemService(BlobStoreManager.class);
final long leaseExpiryMs = System.currentTimeMillis() + BLOB_LEASE_EXPIRY_DURATION_MS;
- final DummyBlobData blobData = new DummyBlobData.Builder(context).setExpiryDurationMs(
+ final FakeBlobData blobData = new FakeBlobData.Builder(context).setExpiryDurationMs(
BLOB_EXPIRY_DURATION_MS).setFileSize(BLOB_FILE_SIZE_BYTES).build();
blobData.prepare();
@@ -1045,7 +1042,7 @@
}
- private void commitBlob(Context context, BlobStoreManager bsm, DummyBlobData blobData)
+ private void commitBlob(Context context, BlobStoreManager bsm, FakeBlobData blobData)
throws Exception {;
final long sessionId = bsm.createSession(blobData.getBlobHandle());
try (BlobStoreManager.Session session = bsm.openSession(sessionId)) {
diff --git a/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/HiddenApiUsedActivity.java b/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/HiddenApiUsedActivity.java
deleted file mode 100644
index 2132f3c..0000000
--- a/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/HiddenApiUsedActivity.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.server.cts.device.statsd;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-import java.lang.reflect.Field;
-
-
-public class HiddenApiUsedActivity extends Activity {
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- try {
- Field field = Activity.class.getDeclaredField("mWindow");
- field.setAccessible(true);
- Object object = field.get(this);
- } catch(NoSuchFieldException e) {
- } catch(IllegalAccessException e) {
- }
- finish();
- }
-
-}
\ No newline at end of file
diff --git a/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/IsolatedProcessService.java b/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/IsolatedProcessService.java
deleted file mode 100644
index 086a3be..0000000
--- a/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/IsolatedProcessService.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-
-package com.android.server.cts.device.statsd;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-import android.util.StatsLog;
-
-public class IsolatedProcessService extends Service {
- private static final String TAG = "IsolatedProcessService";
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- StatsLog.logStart(/*label=*/0);
- return START_NOT_STICKY;
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-}
diff --git a/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java b/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
index c8c02fc..1f14c3a 100644
--- a/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
+++ b/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
@@ -46,7 +46,6 @@
public static final String ACTION_LONG_SLEEP_WHILE_TOP = "action.long_sleep_top";
public static final String ACTION_SHOW_APPLICATION_OVERLAY = "action.show_application_overlay";
public static final String ACTION_SHOW_NOTIFICATION = "action.show_notification";
- public static final String ACTION_CRASH = "action.crash";
public static final String ACTION_CREATE_CHANNEL_GROUP = "action.create_channel_group";
public static final String ACTION_POLL_NETWORK_STATS = "action.poll_network_stats";
@@ -83,9 +82,6 @@
case ACTION_SHOW_NOTIFICATION:
doShowNotification();
break;
- case ACTION_CRASH:
- doCrash();
- break;
case ACTION_CREATE_CHANNEL_GROUP:
doCreateChannelGroup();
break;
@@ -193,9 +189,4 @@
finish();
}
}
-
- @SuppressWarnings("ConstantOverflow")
- private void doCrash() {
- Log.e(TAG, "About to crash the app with 1/0 " + (long) 1 / 0);
- }
}
diff --git a/tests/src/android/cts/statsd/atom/DeviceAtomTestCase.java b/tests/src/android/cts/statsd/atom/DeviceAtomTestCase.java
index 035160f..03facd0 100644
--- a/tests/src/android/cts/statsd/atom/DeviceAtomTestCase.java
+++ b/tests/src/android/cts/statsd/atom/DeviceAtomTestCase.java
@@ -194,7 +194,7 @@
}
/**
- * Required to successfully start a background service from adb in O.
+ * Required to successfully start a background service from adb in Android O.
*/
protected void allowBackgroundServices() throws Exception {
getDevice().executeShellCommand(String.format(
@@ -320,4 +320,9 @@
"settings get global netstats_combine_subtype_enabled").trim();
return output.equals("1");
}
+
+ void setNetworkStatsCombinedSubTypeEnabled(boolean enable) throws Exception {
+ getDevice().executeShellCommand("settings put global netstats_combine_subtype_enabled "
+ + (enable ? "1" : "0"));
+ }
}
diff --git a/tests/src/android/cts/statsd/atom/HostAtomTests.java b/tests/src/android/cts/statsd/atom/HostAtomTests.java
deleted file mode 100644
index ee6f324..0000000
--- a/tests/src/android/cts/statsd/atom/HostAtomTests.java
+++ /dev/null
@@ -1,699 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-package android.cts.statsd.atom;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import android.os.BatteryPluggedStateEnum;
-import android.os.BatteryStatusEnum;
-import android.platform.test.annotations.RestrictedBuildTest;
-import android.server.DeviceIdleModeEnum;
-import android.view.DisplayStateEnum;
-import android.telephony.NetworkTypeEnum;
-
-import com.android.internal.os.StatsdConfigProto.StatsdConfig;
-import com.android.os.AtomsProto.AppBreadcrumbReported;
-import com.android.os.AtomsProto.Atom;
-import com.android.os.AtomsProto.BatterySaverModeStateChanged;
-import com.android.os.AtomsProto.BuildInformation;
-import com.android.os.AtomsProto.ConnectivityStateChanged;
-import com.android.os.AtomsProto.SimSlotState;
-import com.android.os.AtomsProto.SupportedRadioAccessFamily;
-import com.android.os.StatsLog.ConfigMetricsReportList;
-import com.android.os.StatsLog.EventMetricData;
-
-import com.google.common.collect.Range;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Statsd atom tests that are done via adb (hostside).
- */
-public class HostAtomTests extends AtomTestCase {
-
- private static final String TAG = "Statsd.HostAtomTests";
-
- // Either file must exist to read kernel wake lock stats.
- private static final String WAKE_LOCK_FILE = "/proc/wakelocks";
- private static final String WAKE_SOURCES_FILE = "/d/wakeup_sources";
-
- // Bitmask of radio access technologies that all GSM phones should at least partially support
- protected static final long NETWORK_TYPE_BITMASK_GSM_ALL =
- (1 << (NetworkTypeEnum.NETWORK_TYPE_GSM_VALUE - 1))
- | (1 << (NetworkTypeEnum.NETWORK_TYPE_GPRS_VALUE - 1))
- | (1 << (NetworkTypeEnum.NETWORK_TYPE_EDGE_VALUE - 1))
- | (1 << (NetworkTypeEnum.NETWORK_TYPE_UMTS_VALUE - 1))
- | (1 << (NetworkTypeEnum.NETWORK_TYPE_HSDPA_VALUE - 1))
- | (1 << (NetworkTypeEnum.NETWORK_TYPE_HSUPA_VALUE - 1))
- | (1 << (NetworkTypeEnum.NETWORK_TYPE_HSPA_VALUE - 1))
- | (1 << (NetworkTypeEnum.NETWORK_TYPE_HSPAP_VALUE - 1))
- | (1 << (NetworkTypeEnum.NETWORK_TYPE_TD_SCDMA_VALUE - 1))
- | (1 << (NetworkTypeEnum.NETWORK_TYPE_LTE_VALUE - 1))
- | (1 << (NetworkTypeEnum.NETWORK_TYPE_LTE_CA_VALUE - 1))
- | (1 << (NetworkTypeEnum.NETWORK_TYPE_NR_VALUE - 1));
- // Bitmask of radio access technologies that all CDMA phones should at least partially support
- protected static final long NETWORK_TYPE_BITMASK_CDMA_ALL =
- (1 << (NetworkTypeEnum.NETWORK_TYPE_CDMA_VALUE - 1))
- | (1 << (NetworkTypeEnum.NETWORK_TYPE_1XRTT_VALUE - 1))
- | (1 << (NetworkTypeEnum.NETWORK_TYPE_EVDO_0_VALUE - 1))
- | (1 << (NetworkTypeEnum.NETWORK_TYPE_EVDO_A_VALUE - 1))
- | (1 << (NetworkTypeEnum.NETWORK_TYPE_EHRPD_VALUE - 1));
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- }
-
- public void testScreenStateChangedAtom() throws Exception {
- // Setup, make sure the screen is off and turn off AoD if it is on.
- // AoD needs to be turned off because the screen should go into an off state. But, if AoD is
- // on and the device doesn't support STATE_DOZE, the screen sadly goes back to STATE_ON.
- String aodState = getAodState();
- setAodState("0");
- turnScreenOn();
- Thread.sleep(WAIT_TIME_SHORT);
- turnScreenOff();
- Thread.sleep(WAIT_TIME_SHORT);
-
- final int atomTag = Atom.SCREEN_STATE_CHANGED_FIELD_NUMBER;
-
- Set<Integer> screenOnStates = new HashSet<>(
- Arrays.asList(DisplayStateEnum.DISPLAY_STATE_ON_VALUE,
- DisplayStateEnum.DISPLAY_STATE_ON_SUSPEND_VALUE,
- DisplayStateEnum.DISPLAY_STATE_VR_VALUE));
- Set<Integer> screenOffStates = new HashSet<>(
- Arrays.asList(DisplayStateEnum.DISPLAY_STATE_OFF_VALUE,
- DisplayStateEnum.DISPLAY_STATE_DOZE_VALUE,
- DisplayStateEnum.DISPLAY_STATE_DOZE_SUSPEND_VALUE,
- DisplayStateEnum.DISPLAY_STATE_UNKNOWN_VALUE));
-
- // Add state sets to the list in order.
- List<Set<Integer>> stateSet = Arrays.asList(screenOnStates, screenOffStates);
-
- createAndUploadConfig(atomTag);
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Trigger events in same order.
- turnScreenOn();
- Thread.sleep(WAIT_TIME_LONG);
- turnScreenOff();
- Thread.sleep(WAIT_TIME_LONG);
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
- // reset screen to on
- turnScreenOn();
- // Restores AoD to initial state.
- setAodState(aodState);
- // Assert that the events happened in the expected order.
- assertStatesOccurred(stateSet, data, WAIT_TIME_LONG,
- atom -> atom.getScreenStateChanged().getState().getNumber());
- }
-
- public void testChargingStateChangedAtom() throws Exception {
- if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
- // Setup, set charging state to full.
- setChargingState(5);
- Thread.sleep(WAIT_TIME_SHORT);
-
- final int atomTag = Atom.CHARGING_STATE_CHANGED_FIELD_NUMBER;
-
- Set<Integer> batteryUnknownStates = new HashSet<>(
- Arrays.asList(BatteryStatusEnum.BATTERY_STATUS_UNKNOWN_VALUE));
- Set<Integer> batteryChargingStates = new HashSet<>(
- Arrays.asList(BatteryStatusEnum.BATTERY_STATUS_CHARGING_VALUE));
- Set<Integer> batteryDischargingStates = new HashSet<>(
- Arrays.asList(BatteryStatusEnum.BATTERY_STATUS_DISCHARGING_VALUE));
- Set<Integer> batteryNotChargingStates = new HashSet<>(
- Arrays.asList(BatteryStatusEnum.BATTERY_STATUS_NOT_CHARGING_VALUE));
- Set<Integer> batteryFullStates = new HashSet<>(
- Arrays.asList(BatteryStatusEnum.BATTERY_STATUS_FULL_VALUE));
-
- // Add state sets to the list in order.
- List<Set<Integer>> stateSet = Arrays.asList(batteryUnknownStates, batteryChargingStates,
- batteryDischargingStates, batteryNotChargingStates, batteryFullStates);
-
- createAndUploadConfig(atomTag);
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Trigger events in same order.
- setChargingState(1);
- Thread.sleep(WAIT_TIME_SHORT);
- setChargingState(2);
- Thread.sleep(WAIT_TIME_SHORT);
- setChargingState(3);
- Thread.sleep(WAIT_TIME_SHORT);
- setChargingState(4);
- Thread.sleep(WAIT_TIME_SHORT);
- setChargingState(5);
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
-
- // Unfreeze battery state after test
- resetBatteryStatus();
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Assert that the events happened in the expected order.
- assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
- atom -> atom.getChargingStateChanged().getState().getNumber());
- }
-
- public void testPluggedStateChangedAtom() throws Exception {
- if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
- // Setup, unplug device.
- unplugDevice();
- Thread.sleep(WAIT_TIME_SHORT);
-
- final int atomTag = Atom.PLUGGED_STATE_CHANGED_FIELD_NUMBER;
-
- Set<Integer> unpluggedStates = new HashSet<>(
- Arrays.asList(BatteryPluggedStateEnum.BATTERY_PLUGGED_NONE_VALUE));
- Set<Integer> acStates = new HashSet<>(
- Arrays.asList(BatteryPluggedStateEnum.BATTERY_PLUGGED_AC_VALUE));
- Set<Integer> usbStates = new HashSet<>(
- Arrays.asList(BatteryPluggedStateEnum.BATTERY_PLUGGED_USB_VALUE));
- Set<Integer> wirelessStates = new HashSet<>(
- Arrays.asList(BatteryPluggedStateEnum.BATTERY_PLUGGED_WIRELESS_VALUE));
-
- // Add state sets to the list in order.
- List<Set<Integer>> stateSet = Arrays.asList(acStates, unpluggedStates, usbStates,
- unpluggedStates, wirelessStates, unpluggedStates);
-
- createAndUploadConfig(atomTag);
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Trigger events in same order.
- plugInAc();
- Thread.sleep(WAIT_TIME_SHORT);
- unplugDevice();
- Thread.sleep(WAIT_TIME_SHORT);
- plugInUsb();
- Thread.sleep(WAIT_TIME_SHORT);
- unplugDevice();
- Thread.sleep(WAIT_TIME_SHORT);
- plugInWireless();
- Thread.sleep(WAIT_TIME_SHORT);
- unplugDevice();
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
-
- // Unfreeze battery state after test
- resetBatteryStatus();
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Assert that the events happened in the expected order.
- assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
- atom -> atom.getPluggedStateChanged().getState().getNumber());
- }
-
- public void testBatteryLevelChangedAtom() throws Exception {
- if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
- // Setup, set battery level to full.
- setBatteryLevel(100);
- Thread.sleep(WAIT_TIME_SHORT);
-
- final int atomTag = Atom.BATTERY_LEVEL_CHANGED_FIELD_NUMBER;
-
- Set<Integer> batteryLow = new HashSet<>(Arrays.asList(2));
- Set<Integer> battery25p = new HashSet<>(Arrays.asList(25));
- Set<Integer> battery50p = new HashSet<>(Arrays.asList(50));
- Set<Integer> battery75p = new HashSet<>(Arrays.asList(75));
- Set<Integer> batteryFull = new HashSet<>(Arrays.asList(100));
-
- // Add state sets to the list in order.
- List<Set<Integer>> stateSet = Arrays.asList(batteryLow, battery25p, battery50p,
- battery75p, batteryFull);
-
- createAndUploadConfig(atomTag);
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Trigger events in same order.
- setBatteryLevel(2);
- Thread.sleep(WAIT_TIME_SHORT);
- setBatteryLevel(25);
- Thread.sleep(WAIT_TIME_SHORT);
- setBatteryLevel(50);
- Thread.sleep(WAIT_TIME_SHORT);
- setBatteryLevel(75);
- Thread.sleep(WAIT_TIME_SHORT);
- setBatteryLevel(100);
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
-
- // Unfreeze battery state after test
- resetBatteryStatus();
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Assert that the events happened in the expected order.
- assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
- atom -> atom.getBatteryLevelChanged().getBatteryLevel());
- }
-
- public void testDeviceIdleModeStateChangedAtom() throws Exception {
- // Setup, leave doze mode.
- leaveDozeMode();
- Thread.sleep(WAIT_TIME_SHORT);
-
- final int atomTag = Atom.DEVICE_IDLE_MODE_STATE_CHANGED_FIELD_NUMBER;
-
- Set<Integer> dozeOff = new HashSet<>(
- Arrays.asList(DeviceIdleModeEnum.DEVICE_IDLE_MODE_OFF_VALUE));
- Set<Integer> dozeLight = new HashSet<>(
- Arrays.asList(DeviceIdleModeEnum.DEVICE_IDLE_MODE_LIGHT_VALUE));
- Set<Integer> dozeDeep = new HashSet<>(
- Arrays.asList(DeviceIdleModeEnum.DEVICE_IDLE_MODE_DEEP_VALUE));
-
- // Add state sets to the list in order.
- List<Set<Integer>> stateSet = Arrays.asList(dozeLight, dozeDeep, dozeOff);
-
- createAndUploadConfig(atomTag);
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Trigger events in same order.
- enterDozeModeLight();
- Thread.sleep(WAIT_TIME_SHORT);
- enterDozeModeDeep();
- Thread.sleep(WAIT_TIME_SHORT);
- leaveDozeMode();
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();;
-
- // Assert that the events happened in the expected order.
- assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
- atom -> atom.getDeviceIdleModeStateChanged().getState().getNumber());
- }
-
- public void testBatterySaverModeStateChangedAtom() throws Exception {
- if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
- // Setup, turn off battery saver.
- turnBatterySaverOff();
- Thread.sleep(WAIT_TIME_SHORT);
-
- final int atomTag = Atom.BATTERY_SAVER_MODE_STATE_CHANGED_FIELD_NUMBER;
-
- Set<Integer> batterySaverOn = new HashSet<>(
- Arrays.asList(BatterySaverModeStateChanged.State.ON_VALUE));
- Set<Integer> batterySaverOff = new HashSet<>(
- Arrays.asList(BatterySaverModeStateChanged.State.OFF_VALUE));
-
- // Add state sets to the list in order.
- List<Set<Integer>> stateSet = Arrays.asList(batterySaverOn, batterySaverOff);
-
- createAndUploadConfig(atomTag);
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Trigger events in same order.
- turnBatterySaverOn();
- Thread.sleep(WAIT_TIME_LONG);
- turnBatterySaverOff();
- Thread.sleep(WAIT_TIME_LONG);
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
-
- // Assert that the events happened in the expected order.
- assertStatesOccurred(stateSet, data, WAIT_TIME_LONG,
- atom -> atom.getBatterySaverModeStateChanged().getState().getNumber());
- }
-
- @RestrictedBuildTest
- public void testRemainingBatteryCapacity() throws Exception {
- if (!hasFeature(FEATURE_WATCH, false)) return;
- if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.REMAINING_BATTERY_CAPACITY_FIELD_NUMBER, null);
-
- uploadConfig(config);
-
- Thread.sleep(WAIT_TIME_LONG);
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_LONG);
-
- List<Atom> data = getGaugeMetricDataList();
-
- assertThat(data).isNotEmpty();
- Atom atom = data.get(0);
- assertThat(atom.getRemainingBatteryCapacity().hasChargeMicroAmpereHour()).isTrue();
- if (hasBattery()) {
- assertThat(atom.getRemainingBatteryCapacity().getChargeMicroAmpereHour())
- .isGreaterThan(0);
- }
- }
-
- @RestrictedBuildTest
- public void testFullBatteryCapacity() throws Exception {
- if (!hasFeature(FEATURE_WATCH, false)) return;
- if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.FULL_BATTERY_CAPACITY_FIELD_NUMBER, null);
-
- uploadConfig(config);
-
- Thread.sleep(WAIT_TIME_LONG);
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_LONG);
-
- List<Atom> data = getGaugeMetricDataList();
-
- assertThat(data).isNotEmpty();
- Atom atom = data.get(0);
- assertThat(atom.getFullBatteryCapacity().hasCapacityMicroAmpereHour()).isTrue();
- if (hasBattery()) {
- assertThat(atom.getFullBatteryCapacity().getCapacityMicroAmpereHour()).isGreaterThan(0);
- }
- }
-
- public void testBatteryVoltage() throws Exception {
- if (!hasFeature(FEATURE_WATCH, false)) return;
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.BATTERY_VOLTAGE_FIELD_NUMBER, null);
-
- uploadConfig(config);
-
- Thread.sleep(WAIT_TIME_LONG);
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_LONG);
-
- List<Atom> data = getGaugeMetricDataList();
-
- assertThat(data).isNotEmpty();
- Atom atom = data.get(0);
- assertThat(atom.getBatteryVoltage().hasVoltageMillivolt()).isTrue();
- if (hasBattery()) {
- assertThat(atom.getBatteryVoltage().getVoltageMillivolt()).isGreaterThan(0);
- }
- }
-
- // This test is for the pulled battery level atom.
- public void testBatteryLevel() throws Exception {
- if (!hasFeature(FEATURE_WATCH, false)) return;
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.BATTERY_LEVEL_FIELD_NUMBER, null);
-
- uploadConfig(config);
-
- Thread.sleep(WAIT_TIME_LONG);
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_LONG);
-
- List<Atom> data = getGaugeMetricDataList();
-
- assertThat(data).isNotEmpty();
- Atom atom = data.get(0);
- assertThat(atom.getBatteryLevel().hasBatteryLevel()).isTrue();
- if (hasBattery()) {
- assertThat(atom.getBatteryLevel().getBatteryLevel()).isIn(Range.openClosed(0, 100));
- }
- }
-
- // This test is for the pulled battery charge count atom.
- public void testBatteryCycleCount() throws Exception {
- if (!hasFeature(FEATURE_WATCH, false)) return;
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.BATTERY_CYCLE_COUNT_FIELD_NUMBER, null);
-
- uploadConfig(config);
-
- Thread.sleep(WAIT_TIME_LONG);
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_LONG);
-
- List<Atom> data = getGaugeMetricDataList();
-
- assertThat(data).isNotEmpty();
- Atom atom = data.get(0);
- assertThat(atom.getBatteryCycleCount().hasCycleCount()).isTrue();
- if (hasBattery()) {
- assertThat(atom.getBatteryCycleCount().getCycleCount()).isAtLeast(0);
- }
- }
-
- public void testKernelWakelock() throws Exception {
- if (!kernelWakelockStatsExist()) {
- return;
- }
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.KERNEL_WAKELOCK_FIELD_NUMBER, null);
-
- uploadConfig(config);
-
- Thread.sleep(WAIT_TIME_LONG);
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_LONG);
-
- List<Atom> data = getGaugeMetricDataList();
-
- assertThat(data).isNotEmpty();
- for (Atom atom : data) {
- assertThat(atom.getKernelWakelock().hasName()).isTrue();
- assertThat(atom.getKernelWakelock().hasCount()).isTrue();
- assertThat(atom.getKernelWakelock().hasVersion()).isTrue();
- assertThat(atom.getKernelWakelock().getVersion()).isGreaterThan(0);
- assertThat(atom.getKernelWakelock().hasTimeMicros()).isTrue();
- }
- }
-
- // Returns true iff either |WAKE_LOCK_FILE| or |WAKE_SOURCES_FILE| exists.
- private boolean kernelWakelockStatsExist() {
- try {
- return doesFileExist(WAKE_LOCK_FILE) || doesFileExist(WAKE_SOURCES_FILE);
- } catch(Exception e) {
- return false;
- }
- }
-
- public void testWifiActivityInfo() throws Exception {
- if (!hasFeature(FEATURE_WIFI, true)) return;
- if (!hasFeature(FEATURE_WATCH, false)) return;
- if (!checkDeviceFor("checkWifiEnhancedPowerReportingSupported")) return;
-
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.WIFI_ACTIVITY_INFO_FIELD_NUMBER, null);
-
- uploadConfig(config);
-
- Thread.sleep(WAIT_TIME_LONG);
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_LONG);
-
- List<Atom> dataList = getGaugeMetricDataList();
-
- for (Atom atom: dataList) {
- assertThat(atom.getWifiActivityInfo().getTimestampMillis()).isGreaterThan(0L);
- assertThat(atom.getWifiActivityInfo().getStackState()).isAtLeast(0);
- assertThat(atom.getWifiActivityInfo().getControllerIdleTimeMillis()).isGreaterThan(0L);
- assertThat(atom.getWifiActivityInfo().getControllerTxTimeMillis()).isAtLeast(0L);
- assertThat(atom.getWifiActivityInfo().getControllerRxTimeMillis()).isAtLeast(0L);
- assertThat(atom.getWifiActivityInfo().getControllerEnergyUsed()).isAtLeast(0L);
- }
- }
-
- public void testBuildInformation() throws Exception {
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.BUILD_INFORMATION_FIELD_NUMBER, null);
- uploadConfig(config);
-
- Thread.sleep(WAIT_TIME_LONG);
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_LONG);
-
- List<Atom> data = getGaugeMetricDataList();
- assertThat(data).isNotEmpty();
- BuildInformation atom = data.get(0).getBuildInformation();
- assertThat(getProperty("ro.product.brand")).isEqualTo(atom.getBrand());
- assertThat(getProperty("ro.product.name")).isEqualTo(atom.getProduct());
- assertThat(getProperty("ro.product.device")).isEqualTo(atom.getDevice());
- assertThat(getProperty("ro.build.version.release_or_codename")).isEqualTo(atom.getVersionRelease());
- assertThat(getProperty("ro.build.id")).isEqualTo(atom.getId());
- assertThat(getProperty("ro.build.version.incremental"))
- .isEqualTo(atom.getVersionIncremental());
- assertThat(getProperty("ro.build.type")).isEqualTo(atom.getType());
- assertThat(getProperty("ro.build.tags")).isEqualTo(atom.getTags());
- }
-
- public void testOnDevicePowerMeasurement() throws Exception {
- if (!OPTIONAL_TESTS_ENABLED) return;
-
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.ON_DEVICE_POWER_MEASUREMENT_FIELD_NUMBER, null);
-
- uploadConfig(config);
-
- Thread.sleep(WAIT_TIME_LONG);
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_LONG);
-
- List<Atom> dataList = getGaugeMetricDataList();
-
- for (Atom atom: dataList) {
- assertThat(atom.getOnDevicePowerMeasurement().getMeasurementTimestampMillis())
- .isAtLeast(0L);
- assertThat(atom.getOnDevicePowerMeasurement().getEnergyMicrowattSecs()).isAtLeast(0L);
- }
- }
-
- // Explicitly tests if the adb command to log a breadcrumb is working.
- public void testBreadcrumbAdb() throws Exception {
- final int atomTag = Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER;
- createAndUploadConfig(atomTag);
- Thread.sleep(WAIT_TIME_SHORT);
-
- doAppBreadcrumbReportedStart(1);
- Thread.sleep(WAIT_TIME_SHORT);
-
- List<EventMetricData> data = getEventMetricDataList();
- AppBreadcrumbReported atom = data.get(0).getAtom().getAppBreadcrumbReported();
- assertThat(atom.getLabel()).isEqualTo(1);
- assertThat(atom.getState().getNumber()).isEqualTo(AppBreadcrumbReported.State.START_VALUE);
- }
-
- // Test dumpsys stats --proto.
- public void testDumpsysStats() throws Exception {
- final int atomTag = Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER;
- createAndUploadConfig(atomTag);
- Thread.sleep(WAIT_TIME_SHORT);
-
- doAppBreadcrumbReportedStart(1);
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Get the stats incident section.
- List<ConfigMetricsReportList> listList = getReportsFromStatsDataDumpProto();
- assertThat(listList).isNotEmpty();
-
- // Extract the relevent report from the incident section.
- ConfigMetricsReportList ourList = null;
- int hostUid = getHostUid();
- for (ConfigMetricsReportList list : listList) {
- ConfigMetricsReportList.ConfigKey configKey = list.getConfigKey();
- if (configKey.getUid() == hostUid && configKey.getId() == CONFIG_ID) {
- ourList = list;
- break;
- }
- }
- assertWithMessage(String.format("Could not find list for uid=%d id=%d", hostUid, CONFIG_ID))
- .that(ourList).isNotNull();
-
- // Make sure that the report is correct.
- List<EventMetricData> data = getEventMetricDataList(ourList);
- AppBreadcrumbReported atom = data.get(0).getAtom().getAppBreadcrumbReported();
- assertThat(atom.getLabel()).isEqualTo(1);
- assertThat(atom.getState().getNumber()).isEqualTo(AppBreadcrumbReported.State.START_VALUE);
- }
-
- public void testConnectivityStateChange() throws Exception {
- if (!hasFeature(FEATURE_WIFI, true)) return;
- if (!hasFeature(FEATURE_WATCH, false)) return;
- if (!hasFeature(FEATURE_LEANBACK_ONLY, false)) return;
-
- final int atomTag = Atom.CONNECTIVITY_STATE_CHANGED_FIELD_NUMBER;
- createAndUploadConfig(atomTag);
- Thread.sleep(WAIT_TIME_SHORT);
-
- turnOnAirplaneMode();
- // wait long enough for airplane mode events to propagate.
- Thread.sleep(1_200);
- turnOffAirplaneMode();
- // wait long enough for the device to restore connection
- Thread.sleep(13_000);
-
- List<EventMetricData> data = getEventMetricDataList();
- // at least 1 disconnect and 1 connect
- assertThat(data.size()).isAtLeast(2);
- boolean foundDisconnectEvent = false;
- boolean foundConnectEvent = false;
- for (EventMetricData d : data) {
- ConnectivityStateChanged atom = d.getAtom().getConnectivityStateChanged();
- if(atom.getState().getNumber()
- == ConnectivityStateChanged.State.DISCONNECTED_VALUE) {
- foundDisconnectEvent = true;
- }
- if(atom.getState().getNumber()
- == ConnectivityStateChanged.State.CONNECTED_VALUE) {
- foundConnectEvent = true;
- }
- }
- assertThat(foundConnectEvent).isTrue();
- assertThat(foundDisconnectEvent).isTrue();
- }
-
- public void testSimSlotState() throws Exception {
- if (!hasFeature(FEATURE_TELEPHONY, true)) {
- return;
- }
-
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.SIM_SLOT_STATE_FIELD_NUMBER, null);
- uploadConfig(config);
-
- Thread.sleep(WAIT_TIME_LONG);
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_LONG);
-
- List<Atom> data = getGaugeMetricDataList();
- assertThat(data).isNotEmpty();
- SimSlotState atom = data.get(0).getSimSlotState();
- // NOTE: it is possible for devices with telephony support to have no SIM at all
- assertThat(atom.getActiveSlotCount()).isEqualTo(getActiveSimSlotCount());
- assertThat(atom.getSimCount()).isAtMost(getActiveSimCountUpperBound());
- assertThat(atom.getEsimCount()).isAtMost(getActiveEsimCountUpperBound());
- // Above assertions do no necessarily enforce the following, since some are upper bounds
- assertThat(atom.getActiveSlotCount()).isAtLeast(atom.getSimCount());
- assertThat(atom.getSimCount()).isAtLeast(atom.getEsimCount());
- assertThat(atom.getEsimCount()).isAtLeast(0);
- // For GSM phones, at least one slot should be active even if there is no card
- if (hasGsmPhone()) {
- assertThat(atom.getActiveSlotCount()).isAtLeast(1);
- }
- }
-
- public void testSupportedRadioAccessFamily() throws Exception {
- if (!hasFeature(FEATURE_TELEPHONY, true)) {
- return;
- }
-
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.SUPPORTED_RADIO_ACCESS_FAMILY_FIELD_NUMBER, null);
- uploadConfig(config);
-
- Thread.sleep(WAIT_TIME_LONG);
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_LONG);
-
- List<Atom> data = getGaugeMetricDataList();
- assertThat(data).isNotEmpty();
- SupportedRadioAccessFamily atom = data.get(0).getSupportedRadioAccessFamily();
- if (hasGsmPhone()) {
- assertThat(atom.getNetworkTypeBitmask() & NETWORK_TYPE_BITMASK_GSM_ALL)
- .isNotEqualTo(0L);
- }
- if (hasCdmaPhone()) {
- assertThat(atom.getNetworkTypeBitmask() & NETWORK_TYPE_BITMASK_CDMA_ALL)
- .isNotEqualTo(0L);
- }
- }
-}
diff --git a/tests/src/android/cts/statsd/atom/ProcStateAtomTests.java b/tests/src/android/cts/statsd/atom/ProcStateAtomTests.java
deleted file mode 100644
index 230a516..0000000
--- a/tests/src/android/cts/statsd/atom/ProcStateAtomTests.java
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-package android.cts.statsd.atom;
-
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import android.app.ProcessStateEnum; // From enums.proto for atoms.proto's UidProcessStateChanged.
-
-import com.android.os.AtomsProto.Atom;
-import com.android.os.StatsLog.EventMetricData;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-/**
- * Statsd atom tests that are done via app, for atoms that report a uid.
- */
-public class ProcStateAtomTests extends ProcStateTestCase {
-
- private static final String TAG = "Statsd.ProcStateAtomTests";
-
- private static final int WAIT_TIME_FOR_CONFIG_UPDATE_MS = 200;
- // ActivityManager can take a while to register screen state changes, mandating an extra delay.
- private static final int WAIT_TIME_FOR_CONFIG_AND_SCREEN_MS = 1_000;
- private static final int EXTRA_WAIT_TIME_MS = 5_000; // as buffer when proc state changing.
- private static final int STATSD_REPORT_WAIT_TIME_MS = 500; // make sure statsd finishes log.
-
- private static final String FEATURE_WATCH = "android.hardware.type.watch";
-
- // The tests here are using the BatteryStats definition of 'background'.
- private static final Set<Integer> BG_STATES = new HashSet<>(
- Arrays.asList(
- ProcessStateEnum.PROCESS_STATE_IMPORTANT_BACKGROUND_VALUE,
- ProcessStateEnum.PROCESS_STATE_TRANSIENT_BACKGROUND_VALUE,
- ProcessStateEnum.PROCESS_STATE_BACKUP_VALUE,
- ProcessStateEnum.PROCESS_STATE_SERVICE_VALUE,
- ProcessStateEnum.PROCESS_STATE_RECEIVER_VALUE,
- ProcessStateEnum.PROCESS_STATE_HEAVY_WEIGHT_VALUE
- ));
-
- // Using the BatteryStats definition of 'cached', which is why HOME (etc) are considered cached.
- private static final Set<Integer> CACHED_STATES = new HashSet<>(
- Arrays.asList(
- ProcessStateEnum.PROCESS_STATE_HOME_VALUE,
- ProcessStateEnum.PROCESS_STATE_LAST_ACTIVITY_VALUE,
- ProcessStateEnum.PROCESS_STATE_CACHED_ACTIVITY_VALUE,
- ProcessStateEnum.PROCESS_STATE_CACHED_ACTIVITY_CLIENT_VALUE,
- ProcessStateEnum.PROCESS_STATE_CACHED_RECENT_VALUE,
- ProcessStateEnum.PROCESS_STATE_CACHED_EMPTY_VALUE
- ));
-
- private static final Set<Integer> MISC_STATES = new HashSet<>(
- Arrays.asList(
- ProcessStateEnum.PROCESS_STATE_PERSISTENT_VALUE, // TODO: untested
- ProcessStateEnum.PROCESS_STATE_PERSISTENT_UI_VALUE, // TODO: untested
- ProcessStateEnum.PROCESS_STATE_TOP_VALUE,
- ProcessStateEnum.PROCESS_STATE_BOUND_TOP_VALUE, // TODO: untested
- ProcessStateEnum.PROCESS_STATE_BOUND_FOREGROUND_SERVICE_VALUE, // TODO: untested
- ProcessStateEnum.PROCESS_STATE_FOREGROUND_SERVICE_VALUE,
- ProcessStateEnum.PROCESS_STATE_IMPORTANT_FOREGROUND_VALUE,
- ProcessStateEnum.PROCESS_STATE_TOP_SLEEPING_VALUE,
-
- ProcessStateEnum.PROCESS_STATE_UNKNOWN_VALUE,
- ProcessStateEnum.PROCESS_STATE_NONEXISTENT_VALUE
- ));
-
- private static final Set<Integer> ALL_STATES = Stream.of(MISC_STATES, CACHED_STATES, BG_STATES)
- .flatMap(s -> s.stream()).collect(Collectors.toSet());
-
- private static final Function<Atom, Integer> PROC_STATE_FUNCTION =
- atom -> atom.getUidProcessStateChanged().getState().getNumber();
-
- private static final int PROC_STATE_ATOM_TAG = Atom.UID_PROCESS_STATE_CHANGED_FIELD_NUMBER;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- }
-
- public void testForegroundService() throws Exception {
- Set<Integer> onStates = new HashSet<>(Arrays.asList(
- ProcessStateEnum.PROCESS_STATE_FOREGROUND_SERVICE_VALUE));
- Set<Integer> offStates = complement(onStates);
-
- List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
- createAndUploadConfig(PROC_STATE_ATOM_TAG, false); // False: does not use attribution.
- Thread.sleep(WAIT_TIME_FOR_CONFIG_UPDATE_MS);
-
- executeForegroundService();
- final int waitTime = SLEEP_OF_FOREGROUND_SERVICE;
- Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS + EXTRA_WAIT_TIME_MS);
-
- List<EventMetricData> data = getEventMetricDataList();
- popUntilFind(data, onStates, PROC_STATE_FUNCTION); // clear out initial proc states.
- assertStatesOccurred(stateSet, data, waitTime, PROC_STATE_FUNCTION);
- }
-
- public void testForeground() throws Exception {
- Set<Integer> onStates = new HashSet<>(Arrays.asList(
- ProcessStateEnum.PROCESS_STATE_IMPORTANT_FOREGROUND_VALUE));
- // There are no offStates, since the app remains in foreground until killed.
-
- List<Set<Integer>> stateSet = Arrays.asList(onStates); // state sets, in order
- createAndUploadConfig(PROC_STATE_ATOM_TAG, false); // False: does not use attribution.
-
- Thread.sleep(WAIT_TIME_FOR_CONFIG_AND_SCREEN_MS);
-
- executeForegroundActivity(ACTION_SHOW_APPLICATION_OVERLAY);
- final int waitTime = EXTRA_WAIT_TIME_MS + 5_000; // Overlay may need to sit there a while.
- Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS);
-
- List<EventMetricData> data = getEventMetricDataList();
- popUntilFind(data, onStates, PROC_STATE_FUNCTION); // clear out initial proc states.
- assertStatesOccurred(stateSet, data, 0, PROC_STATE_FUNCTION);
- }
-
- public void testBackground() throws Exception {
- Set<Integer> onStates = BG_STATES;
- Set<Integer> offStates = complement(onStates);
-
- List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
- createAndUploadConfig(PROC_STATE_ATOM_TAG, false); // False: does not use attribution.
- Thread.sleep(WAIT_TIME_FOR_CONFIG_UPDATE_MS);
-
- executeBackgroundService(ACTION_BACKGROUND_SLEEP);
- final int waitTime = SLEEP_OF_ACTION_BACKGROUND_SLEEP;
- Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS + EXTRA_WAIT_TIME_MS);
-
- List<EventMetricData> data = getEventMetricDataList();
- popUntilFind(data, onStates, PROC_STATE_FUNCTION); // clear out initial proc states.
- assertStatesOccurred(stateSet, data, waitTime, PROC_STATE_FUNCTION);
- }
-
- public void testTop() throws Exception {
- Set<Integer> onStates = new HashSet<>(Arrays.asList(
- ProcessStateEnum.PROCESS_STATE_TOP_VALUE));
- Set<Integer> offStates = complement(onStates);
-
- List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
- createAndUploadConfig(PROC_STATE_ATOM_TAG, false); // False: does not use attribution.
-
- Thread.sleep(WAIT_TIME_FOR_CONFIG_AND_SCREEN_MS);
-
- executeForegroundActivity(ACTION_SLEEP_WHILE_TOP);
- final int waitTime = SLEEP_OF_ACTION_SLEEP_WHILE_TOP;
- Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS + EXTRA_WAIT_TIME_MS);
-
- List<EventMetricData> data = getEventMetricDataList();
- popUntilFind(data, onStates, PROC_STATE_FUNCTION); // clear out initial proc states.
- assertStatesOccurred(stateSet, data, waitTime, PROC_STATE_FUNCTION);
- }
-
- public void testTopSleeping() throws Exception {
- if (!hasFeature(FEATURE_WATCH, false)) return;
- Set<Integer> onStates = new HashSet<>(Arrays.asList(
- ProcessStateEnum.PROCESS_STATE_TOP_SLEEPING_VALUE));
- Set<Integer> offStates = complement(onStates);
-
- List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
- createAndUploadConfig(PROC_STATE_ATOM_TAG, false); //False: does not use attribution.
-
- turnScreenOn();
- Thread.sleep(WAIT_TIME_FOR_CONFIG_AND_SCREEN_MS);
-
- executeForegroundActivity(ACTION_SLEEP_WHILE_TOP);
- // ASAP, turn off the screen to make proc state -> top_sleeping.
- turnScreenOff();
- final int waitTime = SLEEP_OF_ACTION_SLEEP_WHILE_TOP + EXTRA_WAIT_TIME_MS;
- Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS);
-
- List<EventMetricData> data = getEventMetricDataList();
- popUntilFind(data, new HashSet<>(Arrays.asList(ProcessStateEnum.PROCESS_STATE_TOP_VALUE)),
- PROC_STATE_FUNCTION); // clear out anything prior to it entering TOP.
- popUntilFind(data, onStates, PROC_STATE_FUNCTION); // clear out TOP itself.
- // reset screen back on
- turnScreenOn();
- // Don't check the wait time, since it's up to the system how long top sleeping persists.
- assertStatesOccurred(stateSet, data, 0, PROC_STATE_FUNCTION);
- }
-
- public void testCached() throws Exception {
- Set<Integer> onStates = CACHED_STATES;
- Set<Integer> offStates = complement(onStates);
-
- List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
- createAndUploadConfig(PROC_STATE_ATOM_TAG, false); // False: des not use attribution.
- Thread.sleep(WAIT_TIME_FOR_CONFIG_UPDATE_MS);
-
- // The schedule is as follows
- // #1. The system may do anything it wants, such as moving the app into a cache state.
- // #2. We move the app into the background.
- // #3. The background process ends, so the app definitely moves to a cache state
- // (this is the ultimate goal of the test).
- // #4. We start a foreground activity, moving the app out of cache.
-
- // Start extremely short-lived activity, so app goes into cache state (#1 - #3 above).
- executeBackgroundService(ACTION_END_IMMEDIATELY);
- final int cacheTime = 2_000; // process should be in cached state for up to this long
- Thread.sleep(cacheTime);
- // Now forcibly bring the app out of cache (#4 above).
- executeForegroundActivity(ACTION_SHOW_APPLICATION_OVERLAY);
- // Now check the data *before* the app enters cache again (to avoid another cache event).
-
- List<EventMetricData> data = getEventMetricDataList();
- // First, clear out any incidental cached states of step #1, prior to step #2.
- popUntilFind(data, BG_STATES, PROC_STATE_FUNCTION);
- // Now clear out the bg state from step #2 (since we are interested in the cache after it).
- popUntilFind(data, onStates, PROC_STATE_FUNCTION);
- // The result is that data should start at step #3, definitively in a cached state.
- assertStatesOccurred(stateSet, data, 1_000, PROC_STATE_FUNCTION);
- }
-
- public void testValidityOfStates() throws Exception {
- assertWithMessage("UNKNOWN_TO_PROTO should not be a valid state")
- .that(ALL_STATES).doesNotContain(ProcessStateEnum.PROCESS_STATE_UNKNOWN_TO_PROTO_VALUE);
- }
-
- /** Returns the a set containing elements of a that are not elements of b. */
- private Set<Integer> difference(Set<Integer> a, Set<Integer> b) {
- Set<Integer> result = new HashSet<Integer>(a);
- result.removeAll(b);
- return result;
- }
-
- /** Returns the set of all states that are not in set. */
- private Set<Integer> complement(Set<Integer> set) {
- return difference(ALL_STATES, set);
- }
-}
diff --git a/tests/src/android/cts/statsd/atom/UidAtomTests.java b/tests/src/android/cts/statsd/atom/UidAtomTests.java
deleted file mode 100644
index f41bd86..0000000
--- a/tests/src/android/cts/statsd/atom/UidAtomTests.java
+++ /dev/null
@@ -1,2254 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-package android.cts.statsd.atom;
-
-import static com.android.os.AtomsProto.IntegrityCheckResultReported.Response.ALLOWED;
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import android.app.AppOpEnum;
-import android.net.wifi.WifiModeEnum;
-import android.os.WakeLockLevelEnum;
-import android.server.ErrorSource;
-import android.telephony.NetworkTypeEnum;
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.compatibility.common.util.PropertyUtil;
-import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
-import com.android.internal.os.StatsdConfigProto.StatsdConfig;
-import com.android.os.AtomsProto;
-import com.android.os.AtomsProto.ANROccurred;
-import com.android.os.AtomsProto.AppBreadcrumbReported;
-import com.android.os.AtomsProto.AppCrashOccurred;
-import com.android.os.AtomsProto.AppOps;
-import com.android.os.AtomsProto.AppStartOccurred;
-import com.android.os.AtomsProto.AppUsageEventOccurred;
-import com.android.os.AtomsProto.Atom;
-import com.android.os.AtomsProto.AttributedAppOps;
-import com.android.os.AtomsProto.AttributionNode;
-import com.android.os.AtomsProto.AudioStateChanged;
-import com.android.os.AtomsProto.BinderCalls;
-import com.android.os.AtomsProto.BleScanResultReceived;
-import com.android.os.AtomsProto.BleScanStateChanged;
-import com.android.os.AtomsProto.BlobCommitted;
-import com.android.os.AtomsProto.BlobLeased;
-import com.android.os.AtomsProto.BlobOpened;
-import com.android.os.AtomsProto.CameraStateChanged;
-import com.android.os.AtomsProto.DangerousPermissionState;
-import com.android.os.AtomsProto.DangerousPermissionStateSampled;
-import com.android.os.AtomsProto.DeviceCalculatedPowerBlameUid;
-import com.android.os.AtomsProto.FlashlightStateChanged;
-import com.android.os.AtomsProto.ForegroundServiceAppOpSessionEnded;
-import com.android.os.AtomsProto.ForegroundServiceStateChanged;
-import com.android.os.AtomsProto.GpsScanStateChanged;
-import com.android.os.AtomsProto.HiddenApiUsed;
-import com.android.os.AtomsProto.IntegrityCheckResultReported;
-import com.android.os.AtomsProto.IonHeapSize;
-import com.android.os.AtomsProto.LmkKillOccurred;
-import com.android.os.AtomsProto.LooperStats;
-import com.android.os.AtomsProto.MediaCodecStateChanged;
-import com.android.os.AtomsProto.NotificationReported;
-import com.android.os.AtomsProto.OverlayStateChanged;
-import com.android.os.AtomsProto.PackageNotificationChannelGroupPreferences;
-import com.android.os.AtomsProto.PackageNotificationChannelPreferences;
-import com.android.os.AtomsProto.PackageNotificationPreferences;
-import com.android.os.AtomsProto.PictureInPictureStateChanged;
-import com.android.os.AtomsProto.ProcessMemoryHighWaterMark;
-import com.android.os.AtomsProto.ProcessMemorySnapshot;
-import com.android.os.AtomsProto.ProcessMemoryState;
-import com.android.os.AtomsProto.ScheduledJobStateChanged;
-import com.android.os.AtomsProto.SettingSnapshot;
-import com.android.os.AtomsProto.SyncStateChanged;
-import com.android.os.AtomsProto.TestAtomReported;
-import com.android.os.AtomsProto.UiEventReported;
-import com.android.os.AtomsProto.VibratorStateChanged;
-import com.android.os.AtomsProto.WakelockStateChanged;
-import com.android.os.AtomsProto.WakeupAlarmOccurred;
-import com.android.os.AtomsProto.WifiLockStateChanged;
-import com.android.os.AtomsProto.WifiMulticastLockStateChanged;
-import com.android.os.AtomsProto.WifiScanStateChanged;
-import com.android.os.StatsLog.EventMetricData;
-import com.android.server.notification.SmallHash;
-import com.android.tradefed.log.LogUtil;
-import com.google.common.collect.Range;
-import com.google.protobuf.Descriptors;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-/**
- * Statsd atom tests that are done via app, for atoms that report a uid.
- */
-public class UidAtomTests extends DeviceAtomTestCase {
-
- private static final String TAG = "Statsd.UidAtomTests";
-
- private static final String TEST_PACKAGE_NAME = "com.android.server.cts.device.statsd";
-
- private static final boolean DAVEY_ENABLED = false;
-
- private static final int NUM_APP_OPS = AttributedAppOps.getDefaultInstance().getOp().
- getDescriptorForType().getValues().size() - 1;
-
- private static final String TEST_INSTALL_APK = "CtsStatsdEmptyApp.apk";
- private static final String TEST_INSTALL_APK_BASE = "CtsStatsdEmptySplitApp.apk";
- private static final String TEST_INSTALL_APK_SPLIT = "CtsStatsdEmptySplitApp_pl.apk";
- private static final String TEST_INSTALL_PACKAGE =
- "com.android.cts.device.statsd.emptyapp";
- private static final String TEST_REMOTE_DIR = "/data/local/tmp/statsd";
- private static final String ACTION_SHOW_APPLICATION_OVERLAY = "action.show_application_overlay";
- private static final String ACTION_LONG_SLEEP_WHILE_TOP = "action.long_sleep_top";
-
- private static final int WAIT_TIME_FOR_CONFIG_UPDATE_MS = 200;
- private static final int EXTRA_WAIT_TIME_MS = 5_000; // as buffer when app starting/stopping.
- private static final int STATSD_REPORT_WAIT_TIME_MS = 500; // make sure statsd finishes log.
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- }
-
- @Override
- protected void tearDown() throws Exception {
- resetBatteryStatus();
- super.tearDown();
- }
-
- public void testLmkKillOccurred() throws Exception {
- if (!"true".equals(getProperty("ro.lmk.log_stats"))) {
- return;
- }
-
- final int atomTag = Atom.LMK_KILL_OCCURRED_FIELD_NUMBER;
- createAndUploadConfig(atomTag, false);
-
- Thread.sleep(WAIT_TIME_SHORT);
-
- executeBackgroundService(ACTION_LMK);
- Thread.sleep(15_000);
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
-
- assertThat(data).hasSize(1);
- assertThat(data.get(0).getAtom().hasLmkKillOccurred()).isTrue();
- LmkKillOccurred atom = data.get(0).getAtom().getLmkKillOccurred();
- assertThat(atom.getUid()).isEqualTo(getUid());
- assertThat(atom.getProcessName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
- assertThat(atom.getOomAdjScore()).isAtLeast(500);
- }
-
- public void testAppCrashOccurred() throws Exception {
- final int atomTag = Atom.APP_CRASH_OCCURRED_FIELD_NUMBER;
- createAndUploadConfig(atomTag, false);
- Thread.sleep(WAIT_TIME_SHORT);
-
- runActivity("StatsdCtsForegroundActivity", "action", "action.crash");
-
- Thread.sleep(WAIT_TIME_SHORT);
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
-
- AppCrashOccurred atom = data.get(0).getAtom().getAppCrashOccurred();
- assertThat(atom.getEventType()).isEqualTo("crash");
- assertThat(atom.getIsInstantApp().getNumber())
- .isEqualTo(AppCrashOccurred.InstantApp.FALSE_VALUE);
- assertThat(atom.getForegroundState().getNumber())
- .isEqualTo(AppCrashOccurred.ForegroundState.FOREGROUND_VALUE);
- assertThat(atom.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
- }
-
- public void testAppStartOccurred() throws Exception {
- final int atomTag = Atom.APP_START_OCCURRED_FIELD_NUMBER;
-
- createAndUploadConfig(atomTag, false);
- Thread.sleep(WAIT_TIME_SHORT);
-
- runActivity("StatsdCtsForegroundActivity", "action", "action.sleep_top", 3_500);
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
-
- assertThat(data).hasSize(1);
- AppStartOccurred atom = data.get(0).getAtom().getAppStartOccurred();
- assertThat(atom.getPkgName()).isEqualTo(TEST_PACKAGE_NAME);
- assertThat(atom.getActivityName())
- .isEqualTo("com.android.server.cts.device.statsd.StatsdCtsForegroundActivity");
- assertThat(atom.getIsInstantApp()).isFalse();
- assertThat(atom.getActivityStartMillis()).isGreaterThan(0L);
- assertThat(atom.getTransitionDelayMillis()).isGreaterThan(0);
- }
-
- public void testAudioState() throws Exception {
- if (!hasFeature(FEATURE_AUDIO_OUTPUT, true)) return;
-
- final int atomTag = Atom.AUDIO_STATE_CHANGED_FIELD_NUMBER;
- final String name = "testAudioState";
-
- Set<Integer> onState = new HashSet<>(
- Arrays.asList(AudioStateChanged.State.ON_VALUE));
- Set<Integer> offState = new HashSet<>(
- Arrays.asList(AudioStateChanged.State.OFF_VALUE));
-
- // Add state sets to the list in order.
- List<Set<Integer>> stateSet = Arrays.asList(onState, offState);
-
- createAndUploadConfig(atomTag, true); // True: uses attribution.
- Thread.sleep(WAIT_TIME_SHORT);
-
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", name);
-
- Thread.sleep(WAIT_TIME_SHORT);
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
-
- // Because the timestamp is truncated, we skip checking time differences between state
- // changes.
- assertStatesOccurred(stateSet, data, 0,
- atom -> atom.getAudioStateChanged().getState().getNumber());
-
- // Check that timestamp is truncated
- for (EventMetricData metric : data) {
- long elapsedTimestampNs = metric.getElapsedTimestampNanos();
- assertTimestampIsTruncated(elapsedTimestampNs);
- }
- }
-
- public void testBleScan() throws Exception {
- if (!hasFeature(FEATURE_BLUETOOTH_LE, true)) return;
-
- final int atom = Atom.BLE_SCAN_STATE_CHANGED_FIELD_NUMBER;
- final int field = BleScanStateChanged.STATE_FIELD_NUMBER;
- final int stateOn = BleScanStateChanged.State.ON_VALUE;
- final int stateOff = BleScanStateChanged.State.OFF_VALUE;
- final int minTimeDiffMillis = 1_500;
- final int maxTimeDiffMillis = 3_000;
-
- List<EventMetricData> data = doDeviceMethodOnOff("testBleScanUnoptimized", atom, field,
- stateOn, stateOff, minTimeDiffMillis, maxTimeDiffMillis, true);
-
- BleScanStateChanged a0 = data.get(0).getAtom().getBleScanStateChanged();
- BleScanStateChanged a1 = data.get(1).getAtom().getBleScanStateChanged();
- assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
- assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
- }
-
- public void testBleUnoptimizedScan() throws Exception {
- if (!hasFeature(FEATURE_BLUETOOTH_LE, true)) return;
-
- final int atom = Atom.BLE_SCAN_STATE_CHANGED_FIELD_NUMBER;
- final int field = BleScanStateChanged.STATE_FIELD_NUMBER;
- final int stateOn = BleScanStateChanged.State.ON_VALUE;
- final int stateOff = BleScanStateChanged.State.OFF_VALUE;
- final int minTimeDiffMillis = 1_500;
- final int maxTimeDiffMillis = 3_000;
-
- List<EventMetricData> data = doDeviceMethodOnOff("testBleScanUnoptimized", atom, field,
- stateOn, stateOff, minTimeDiffMillis, maxTimeDiffMillis, true);
-
- BleScanStateChanged a0 = data.get(0).getAtom().getBleScanStateChanged();
- assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
- assertThat(a0.getIsFiltered()).isFalse();
- assertThat(a0.getIsFirstMatch()).isFalse();
- assertThat(a0.getIsOpportunistic()).isFalse();
- BleScanStateChanged a1 = data.get(1).getAtom().getBleScanStateChanged();
- assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
- assertThat(a1.getIsFiltered()).isFalse();
- assertThat(a1.getIsFirstMatch()).isFalse();
- assertThat(a1.getIsOpportunistic()).isFalse();
-
-
- // Now repeat the test for opportunistic scanning and make sure it is reported correctly.
- data = doDeviceMethodOnOff("testBleScanOpportunistic", atom, field,
- stateOn, stateOff, minTimeDiffMillis, maxTimeDiffMillis, true);
-
- a0 = data.get(0).getAtom().getBleScanStateChanged();
- assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
- assertThat(a0.getIsFiltered()).isFalse();
- assertThat(a0.getIsFirstMatch()).isFalse();
- assertThat(a0.getIsOpportunistic()).isTrue(); // This scan is opportunistic.
- a1 = data.get(1).getAtom().getBleScanStateChanged();
- assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
- assertThat(a1.getIsFiltered()).isFalse();
- assertThat(a1.getIsFirstMatch()).isFalse();
- assertThat(a1.getIsOpportunistic()).isTrue();
- }
-
- public void testBleScanResult() throws Exception {
- if (!hasFeature(FEATURE_BLUETOOTH_LE, true)) return;
-
- final int atom = Atom.BLE_SCAN_RESULT_RECEIVED_FIELD_NUMBER;
- final int field = BleScanResultReceived.NUM_RESULTS_FIELD_NUMBER;
-
- StatsdConfig.Builder conf = createConfigBuilder();
- addAtomEvent(conf, atom, createFvm(field).setGteInt(0));
- List<EventMetricData> data = doDeviceMethod("testBleScanResult", conf);
-
- assertThat(data.size()).isAtLeast(1);
- BleScanResultReceived a0 = data.get(0).getAtom().getBleScanResultReceived();
- assertThat(a0.getNumResults()).isAtLeast(1);
- }
-
- public void testHiddenApiUsed() throws Exception {
- String oldRate = getDevice().executeShellCommand(
- "device_config get app_compat hidden_api_access_statslog_sampling_rate").trim();
-
- getDevice().executeShellCommand(
- "device_config put app_compat hidden_api_access_statslog_sampling_rate 65536");
-
- Thread.sleep(WAIT_TIME_SHORT);
-
- try {
- final int atomTag = Atom.HIDDEN_API_USED_FIELD_NUMBER;
-
- createAndUploadConfig(atomTag, false);
-
- runActivity("HiddenApiUsedActivity", null, null, 2_500);
-
- List<EventMetricData> data = getEventMetricDataList();
- assertThat(data).hasSize(1);
-
- HiddenApiUsed atom = data.get(0).getAtom().getHiddenApiUsed();
-
- int uid = getUid();
- assertThat(atom.getUid()).isEqualTo(uid);
- assertThat(atom.getAccessDenied()).isFalse();
- assertThat(atom.getSignature())
- .isEqualTo("Landroid/app/Activity;->mWindow:Landroid/view/Window;");
- } finally {
- if (!oldRate.equals("null")) {
- getDevice().executeShellCommand(
- "device_config put app_compat hidden_api_access_statslog_sampling_rate "
- + oldRate);
- } else {
- getDevice().executeShellCommand(
- "device_config delete hidden_api_access_statslog_sampling_rate");
- }
- }
- }
-
- public void testCameraState() throws Exception {
- if (!hasFeature(FEATURE_CAMERA, true) && !hasFeature(FEATURE_CAMERA_FRONT, true)) return;
-
- final int atomTag = Atom.CAMERA_STATE_CHANGED_FIELD_NUMBER;
- Set<Integer> cameraOn = new HashSet<>(Arrays.asList(CameraStateChanged.State.ON_VALUE));
- Set<Integer> cameraOff = new HashSet<>(Arrays.asList(CameraStateChanged.State.OFF_VALUE));
-
- // Add state sets to the list in order.
- List<Set<Integer>> stateSet = Arrays.asList(cameraOn, cameraOff);
-
- createAndUploadConfig(atomTag, true); // True: uses attribution.
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testCameraState");
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
-
- // Assert that the events happened in the expected order.
- assertStatesOccurred(stateSet, data, WAIT_TIME_LONG,
- atom -> atom.getCameraStateChanged().getState().getNumber());
- }
-
- public void testCpuTimePerUid() throws Exception {
- if (!hasFeature(FEATURE_WATCH, false)) return;
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.CPU_TIME_PER_UID_FIELD_NUMBER, null);
-
- uploadConfig(config);
-
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testSimpleCpu");
-
- Thread.sleep(WAIT_TIME_SHORT);
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_LONG);
-
- List<Atom> atomList = getGaugeMetricDataList();
-
- // TODO: We don't have atom matching on gauge yet. Let's refactor this after that feature is
- // implemented.
- boolean found = false;
- int uid = getUid();
- for (Atom atom : atomList) {
- if (atom.getCpuTimePerUid().getUid() == uid) {
- found = true;
- assertThat(atom.getCpuTimePerUid().getUserTimeMicros()).isGreaterThan(0L);
- assertThat(atom.getCpuTimePerUid().getSysTimeMicros()).isGreaterThan(0L);
- }
- }
- assertWithMessage(String.format("did not find uid %d", uid)).that(found).isTrue();
- }
-
- public void testDeviceCalculatedPowerUse() throws Exception {
- if (!hasFeature(FEATURE_LEANBACK_ONLY, false)) return;
-
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.DEVICE_CALCULATED_POWER_USE_FIELD_NUMBER, null);
- uploadConfig(config);
- unplugDevice();
-
- Thread.sleep(WAIT_TIME_LONG);
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testSimpleCpu");
- Thread.sleep(WAIT_TIME_SHORT);
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_LONG);
-
- Atom atom = getGaugeMetricDataList().get(0);
- assertThat(atom.getDeviceCalculatedPowerUse().getComputedPowerNanoAmpSecs())
- .isGreaterThan(0L);
- }
-
-
- public void testDeviceCalculatedPowerBlameUid() throws Exception {
- if (!hasFeature(FEATURE_LEANBACK_ONLY, false)) return;
- if (!hasBattery()) {
- return;
- }
-
- String kernelVersion = getDevice().executeShellCommand("uname -r");
- if (kernelVersion.contains("3.18")) {
- LogUtil.CLog.d("Skipping calculated power blame uid test.");
- return;
- }
-
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config,
- Atom.DEVICE_CALCULATED_POWER_BLAME_UID_FIELD_NUMBER, null);
- uploadConfig(config);
- unplugDevice();
-
- Thread.sleep(WAIT_TIME_LONG);
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testSimpleCpu");
- Thread.sleep(WAIT_TIME_SHORT);
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_LONG);
-
- List<Atom> atomList = getGaugeMetricDataList();
- boolean uidFound = false;
- int uid = getUid();
- long uidPower = 0;
- for (Atom atom : atomList) {
- DeviceCalculatedPowerBlameUid item = atom.getDeviceCalculatedPowerBlameUid();
- if (item.getUid() == uid) {
- assertWithMessage(String.format("Found multiple power values for uid %d", uid))
- .that(uidFound).isFalse();
- uidFound = true;
- uidPower = item.getPowerNanoAmpSecs();
- }
- }
- assertWithMessage(String.format("No power value for uid %d", uid)).that(uidFound).isTrue();
- assertWithMessage(String.format("Non-positive power value for uid %d", uid))
- .that(uidPower).isGreaterThan(0L);
- }
-
- public void testDavey() throws Exception {
- if (!DAVEY_ENABLED ) return;
- long MAX_DURATION = 2000;
- long MIN_DURATION = 750;
- final int atomTag = Atom.DAVEY_OCCURRED_FIELD_NUMBER;
- createAndUploadConfig(atomTag, false); // UID is logged without attribution node
-
- runActivity("DaveyActivity", null, null);
-
- List<EventMetricData> data = getEventMetricDataList();
- assertThat(data).hasSize(1);
- long duration = data.get(0).getAtom().getDaveyOccurred().getJankDurationMillis();
- assertWithMessage("Incorrect jank duration")
- .that(duration).isIn(Range.closed(MIN_DURATION, MAX_DURATION));
- }
-
- public void testFlashlightState() throws Exception {
- if (!hasFeature(FEATURE_CAMERA_FLASH, true)) return;
-
- final int atomTag = Atom.FLASHLIGHT_STATE_CHANGED_FIELD_NUMBER;
- final String name = "testFlashlight";
-
- Set<Integer> flashlightOn = new HashSet<>(
- Arrays.asList(FlashlightStateChanged.State.ON_VALUE));
- Set<Integer> flashlightOff = new HashSet<>(
- Arrays.asList(FlashlightStateChanged.State.OFF_VALUE));
-
- // Add state sets to the list in order.
- List<Set<Integer>> stateSet = Arrays.asList(flashlightOn, flashlightOff);
-
- createAndUploadConfig(atomTag, true); // True: uses attribution.
- Thread.sleep(WAIT_TIME_SHORT);
-
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", name);
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
-
- // Assert that the events happened in the expected order.
- assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
- atom -> atom.getFlashlightStateChanged().getState().getNumber());
- }
-
- public void testForegroundServiceState() throws Exception {
- final int atomTag = Atom.FOREGROUND_SERVICE_STATE_CHANGED_FIELD_NUMBER;
- final String name = "testForegroundService";
-
- Set<Integer> enterForeground = new HashSet<>(
- Arrays.asList(ForegroundServiceStateChanged.State.ENTER_VALUE));
- Set<Integer> exitForeground = new HashSet<>(
- Arrays.asList(ForegroundServiceStateChanged.State.EXIT_VALUE));
-
- // Add state sets to the list in order.
- List<Set<Integer>> stateSet = Arrays.asList(enterForeground, exitForeground);
-
- createAndUploadConfig(atomTag, false);
- Thread.sleep(WAIT_TIME_SHORT);
-
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", name);
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
-
- // Assert that the events happened in the expected order.
- assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
- atom -> atom.getForegroundServiceStateChanged().getState().getNumber());
- }
-
-
- public void testForegroundServiceAccessAppOp() throws Exception {
- final int atomTag = Atom.FOREGROUND_SERVICE_APP_OP_SESSION_ENDED_FIELD_NUMBER;
- final String name = "testForegroundServiceAccessAppOp";
-
- createAndUploadConfig(atomTag, false);
- Thread.sleep(WAIT_TIME_SHORT);
-
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", name);
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
-
- assertWithMessage("Wrong atom size").that(data.size()).isEqualTo(3);
- for (int i = 0; i < data.size(); i++) {
- ForegroundServiceAppOpSessionEnded atom
- = data.get(i).getAtom().getForegroundServiceAppOpSessionEnded();
- final int opName = atom.getAppOpName().getNumber();
- final int acceptances = atom.getCountOpsAccepted();
- final int rejections = atom.getCountOpsRejected();
- final int count = acceptances + rejections;
- int expectedCount = 0;
- switch (opName) {
- case AppOpEnum.APP_OP_CAMERA_VALUE:
- expectedCount = 3;
- break;
- case AppOpEnum.APP_OP_FINE_LOCATION_VALUE:
- expectedCount = 1;
- break;
- case AppOpEnum.APP_OP_RECORD_AUDIO_VALUE:
- expectedCount = 2;
- break;
- case AppOpEnum.APP_OP_COARSE_LOCATION_VALUE:
- // fall-through
- default:
- fail("Unexpected opName " + opName);
- }
- assertWithMessage("Wrong count for " + opName).that(count).isEqualTo(expectedCount);
- }
- }
-
- public void testGpsScan() throws Exception {
- if (!hasFeature(FEATURE_LOCATION_GPS, true)) return;
- // Whitelist this app against background location request throttling
- String origWhitelist = getDevice().executeShellCommand(
- "settings get global location_background_throttle_package_whitelist").trim();
- getDevice().executeShellCommand(String.format(
- "settings put global location_background_throttle_package_whitelist %s",
- DEVICE_SIDE_TEST_PACKAGE));
-
- try {
- final int atom = Atom.GPS_SCAN_STATE_CHANGED_FIELD_NUMBER;
- final int key = GpsScanStateChanged.STATE_FIELD_NUMBER;
- final int stateOn = GpsScanStateChanged.State.ON_VALUE;
- final int stateOff = GpsScanStateChanged.State.OFF_VALUE;
- final int minTimeDiffMillis = 500;
- final int maxTimeDiffMillis = 60_000;
-
- List<EventMetricData> data = doDeviceMethodOnOff("testGpsScan", atom, key,
- stateOn, stateOff, minTimeDiffMillis, maxTimeDiffMillis, true);
-
- GpsScanStateChanged a0 = data.get(0).getAtom().getGpsScanStateChanged();
- GpsScanStateChanged a1 = data.get(1).getAtom().getGpsScanStateChanged();
- assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
- assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
- } finally {
- if ("null".equals(origWhitelist) || "".equals(origWhitelist)) {
- getDevice().executeShellCommand(
- "settings delete global location_background_throttle_package_whitelist");
- } else {
- getDevice().executeShellCommand(String.format(
- "settings put global location_background_throttle_package_whitelist %s",
- origWhitelist));
- }
- }
- }
-
- public void testGnssStats() throws Exception {
- // Get GnssMetrics as a simple gauge metric.
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.GNSS_STATS_FIELD_NUMBER, null);
- uploadConfig(config);
- Thread.sleep(WAIT_TIME_SHORT);
-
- if (!hasFeature(FEATURE_LOCATION_GPS, true)) return;
- // Whitelist this app against background location request throttling
- String origWhitelist = getDevice().executeShellCommand(
- "settings get global location_background_throttle_package_whitelist").trim();
- getDevice().executeShellCommand(String.format(
- "settings put global location_background_throttle_package_whitelist %s",
- DEVICE_SIDE_TEST_PACKAGE));
-
- try {
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testGpsStatus");
-
- Thread.sleep(WAIT_TIME_LONG);
- // Trigger a pull and wait for new pull before killing the process.
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_LONG);
-
- // Assert about GnssMetrics for the test app.
- List<Atom> atoms = getGaugeMetricDataList();
-
- boolean found = false;
- for (Atom atom : atoms) {
- AtomsProto.GnssStats state = atom.getGnssStats();
- found = true;
- if ((state.getSvStatusReports() > 0 || state.getL5SvStatusReports() > 0)
- && state.getLocationReports() == 0) {
- // Device is detected to be indoors and not able to acquire location.
- // flaky test device
- break;
- }
- assertThat(state.getLocationReports()).isGreaterThan((long) 0);
- assertThat(state.getLocationFailureReports()).isAtLeast((long) 0);
- assertThat(state.getTimeToFirstFixReports()).isGreaterThan((long) 0);
- assertThat(state.getTimeToFirstFixMillis()).isGreaterThan((long) 0);
- assertThat(state.getPositionAccuracyReports()).isGreaterThan((long) 0);
- assertThat(state.getPositionAccuracyMeters()).isGreaterThan((long) 0);
- assertThat(state.getTopFourAverageCn0Reports()).isGreaterThan((long) 0);
- assertThat(state.getTopFourAverageCn0DbMhz()).isGreaterThan((long) 0);
- assertThat(state.getL5TopFourAverageCn0Reports()).isAtLeast((long) 0);
- assertThat(state.getL5TopFourAverageCn0DbMhz()).isAtLeast((long) 0);
- assertThat(state.getSvStatusReports()).isAtLeast((long) 0);
- assertThat(state.getSvStatusReportsUsedInFix()).isAtLeast((long) 0);
- assertThat(state.getL5SvStatusReports()).isAtLeast((long) 0);
- assertThat(state.getL5SvStatusReportsUsedInFix()).isAtLeast((long) 0);
- }
- assertWithMessage(String.format("Did not find a matching atom"))
- .that(found).isTrue();
- } finally {
- if ("null".equals(origWhitelist) || "".equals(origWhitelist)) {
- getDevice().executeShellCommand(
- "settings delete global location_background_throttle_package_whitelist");
- } else {
- getDevice().executeShellCommand(String.format(
- "settings put global location_background_throttle_package_whitelist %s",
- origWhitelist));
- }
- }
- }
-
- public void testMediaCodecActivity() throws Exception {
- if (!hasFeature(FEATURE_WATCH, false)) return;
- final int atomTag = Atom.MEDIA_CODEC_STATE_CHANGED_FIELD_NUMBER;
-
- // 5 seconds. Starting video tends to be much slower than most other
- // tests on slow devices. This is unfortunate, because it leaves a
- // really big slop in assertStatesOccurred. It would be better if
- // assertStatesOccurred had a tighter range on large timeouts.
- final int waitTime = 5000;
-
- // From {@link VideoPlayerActivity#DELAY_MILLIS}
- final int videoDuration = 2000;
-
- Set<Integer> onState = new HashSet<>(
- Arrays.asList(MediaCodecStateChanged.State.ON_VALUE));
- Set<Integer> offState = new HashSet<>(
- Arrays.asList(MediaCodecStateChanged.State.OFF_VALUE));
-
- // Add state sets to the list in order.
- List<Set<Integer>> stateSet = Arrays.asList(onState, offState);
-
- createAndUploadConfig(atomTag, true); // True: uses attribution.
- Thread.sleep(WAIT_TIME_SHORT);
-
- runActivity("VideoPlayerActivity", "action", "action.play_video",
- waitTime);
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
-
- // Assert that the events happened in the expected order.
- assertStatesOccurred(stateSet, data, videoDuration,
- atom -> atom.getMediaCodecStateChanged().getState().getNumber());
- }
-
- public void testOverlayState() throws Exception {
- if (!hasFeature(FEATURE_WATCH, false)) return;
- final int atomTag = Atom.OVERLAY_STATE_CHANGED_FIELD_NUMBER;
-
- Set<Integer> entered = new HashSet<>(
- Arrays.asList(OverlayStateChanged.State.ENTERED_VALUE));
- Set<Integer> exited = new HashSet<>(
- Arrays.asList(OverlayStateChanged.State.EXITED_VALUE));
-
- // Add state sets to the list in order.
- List<Set<Integer>> stateSet = Arrays.asList(entered, exited);
-
- createAndUploadConfig(atomTag, false);
-
- runActivity("StatsdCtsForegroundActivity", "action", "action.show_application_overlay",
- 5_000);
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
-
- // Assert that the events happened in the expected order.
- // The overlay box should appear about 2sec after the app start
- assertStatesOccurred(stateSet, data, 0,
- atom -> atom.getOverlayStateChanged().getState().getNumber());
- }
-
- public void testPictureInPictureState() throws Exception {
- String supported = getDevice().executeShellCommand("am supports-multiwindow");
- if (!hasFeature(FEATURE_WATCH, false) ||
- !hasFeature(FEATURE_PICTURE_IN_PICTURE, true) ||
- !supported.contains("true")) {
- LogUtil.CLog.d("Skipping picture in picture atom test.");
- return;
- }
-
- StatsdConfig.Builder conf = createConfigBuilder();
- // PictureInPictureStateChanged atom is used prior to rvc-qpr
- addAtomEvent(conf, Atom.PICTURE_IN_PICTURE_STATE_CHANGED_FIELD_NUMBER,
- /*useAttribution=*/false);
- // Picture-in-picture logs' been migrated to UiEvent since rvc-qpr
- FieldValueMatcher.Builder pkgMatcher = createFvm(UiEventReported.PACKAGE_NAME_FIELD_NUMBER)
- .setEqString(DEVICE_SIDE_TEST_PACKAGE);
- addAtomEvent(conf, Atom.UI_EVENT_REPORTED_FIELD_NUMBER, Arrays.asList(pkgMatcher));
- uploadConfig(conf);
-
- LogUtil.CLog.d("Playing video in Picture-in-Picture mode");
- runActivity("VideoPlayerActivity", "action", "action.play_video_picture_in_picture_mode");
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
-
- // Filter out the PictureInPictureStateChanged and UiEventReported atom
- List<EventMetricData> pictureInPictureStateChangedData = data.stream()
- .filter(e -> e.getAtom().hasPictureInPictureStateChanged())
- .collect(Collectors.toList());
- List<EventMetricData> uiEventReportedData = data.stream()
- .filter(e -> e.getAtom().hasUiEventReported())
- .collect(Collectors.toList());
-
- if (!pictureInPictureStateChangedData.isEmpty()) {
- LogUtil.CLog.d("Assert using PictureInPictureStateChanged");
- Set<Integer> entered = new HashSet<>(
- Arrays.asList(PictureInPictureStateChanged.State.ENTERED_VALUE));
- List<Set<Integer>> stateSet = Arrays.asList(entered);
- assertStatesOccurred(stateSet, data, WAIT_TIME_LONG,
- atom -> atom.getPictureInPictureStateChanged().getState().getNumber());
- } else if (!uiEventReportedData.isEmpty()) {
- LogUtil.CLog.d("Assert using UiEventReported");
- // See PipUiEventEnum for definitions
- final int enterPipEventId = 603;
- // Assert that log for entering PiP happens exactly once, we do not use
- // assertStateOccurred here since PiP may log something else when activity finishes.
- List<EventMetricData> entered = uiEventReportedData.stream()
- .filter(e -> e.getAtom().getUiEventReported().getEventId() == enterPipEventId)
- .collect(Collectors.toList());
- assertThat(entered).hasSize(1);
- } else {
- fail("No logging event from PictureInPictureStateChanged nor UiEventReported");
- }
- }
-
- public void testScheduledJobState() throws Exception {
- String expectedName = "com.android.server.cts.device.statsd/.StatsdJobService";
- final int atomTag = Atom.SCHEDULED_JOB_STATE_CHANGED_FIELD_NUMBER;
- Set<Integer> jobSchedule = new HashSet<>(
- Arrays.asList(ScheduledJobStateChanged.State.SCHEDULED_VALUE));
- Set<Integer> jobOn = new HashSet<>(
- Arrays.asList(ScheduledJobStateChanged.State.STARTED_VALUE));
- Set<Integer> jobOff = new HashSet<>(
- Arrays.asList(ScheduledJobStateChanged.State.FINISHED_VALUE));
-
- // Add state sets to the list in order.
- List<Set<Integer>> stateSet = Arrays.asList(jobSchedule, jobOn, jobOff);
-
- createAndUploadConfig(atomTag, true); // True: uses attribution.
- allowImmediateSyncs();
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testScheduledJob");
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
-
- assertStatesOccurred(stateSet, data, 0,
- atom -> atom.getScheduledJobStateChanged().getState().getNumber());
-
- for (EventMetricData e : data) {
- assertThat(e.getAtom().getScheduledJobStateChanged().getJobName())
- .isEqualTo(expectedName);
- }
- }
-
- //Note: this test does not have uid, but must run on the device
- public void testScreenBrightness() throws Exception {
- int initialBrightness = getScreenBrightness();
- boolean isInitialManual = isScreenBrightnessModeManual();
- setScreenBrightnessMode(true);
- setScreenBrightness(200);
- Thread.sleep(WAIT_TIME_LONG);
-
- final int atomTag = Atom.SCREEN_BRIGHTNESS_CHANGED_FIELD_NUMBER;
-
- Set<Integer> screenMin = new HashSet<>(Arrays.asList(47));
- Set<Integer> screen100 = new HashSet<>(Arrays.asList(100));
- Set<Integer> screen200 = new HashSet<>(Arrays.asList(198));
- // Set<Integer> screenMax = new HashSet<>(Arrays.asList(255));
-
- // Add state sets to the list in order.
- List<Set<Integer>> stateSet = Arrays.asList(screenMin, screen100, screen200);
-
- createAndUploadConfig(atomTag);
- Thread.sleep(WAIT_TIME_SHORT);
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testScreenBrightness");
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
-
- // Restore initial screen brightness
- setScreenBrightness(initialBrightness);
- setScreenBrightnessMode(isInitialManual);
-
- popUntilFind(data, screenMin, atom->atom.getScreenBrightnessChanged().getLevel());
- popUntilFindFromEnd(data, screen200, atom->atom.getScreenBrightnessChanged().getLevel());
- // Assert that the events happened in the expected order.
- assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
- atom -> atom.getScreenBrightnessChanged().getLevel());
- }
- public void testSyncState() throws Exception {
- final int atomTag = Atom.SYNC_STATE_CHANGED_FIELD_NUMBER;
- Set<Integer> syncOn = new HashSet<>(Arrays.asList(SyncStateChanged.State.ON_VALUE));
- Set<Integer> syncOff = new HashSet<>(Arrays.asList(SyncStateChanged.State.OFF_VALUE));
-
- // Add state sets to the list in order.
- List<Set<Integer>> stateSet = Arrays.asList(syncOn, syncOff, syncOn, syncOff);
-
- createAndUploadConfig(atomTag, true);
- allowImmediateSyncs();
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testSyncState");
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
-
- // Assert that the events happened in the expected order.
- assertStatesOccurred(stateSet, data,
- /* wait = */ 0 /* don't verify time differences between state changes */,
- atom -> atom.getSyncStateChanged().getState().getNumber());
- }
-
- public void testVibratorState() throws Exception {
- if (!checkDeviceFor("checkVibratorSupported")) return;
-
- final int atomTag = Atom.VIBRATOR_STATE_CHANGED_FIELD_NUMBER;
- final String name = "testVibratorState";
-
- Set<Integer> onState = new HashSet<>(
- Arrays.asList(VibratorStateChanged.State.ON_VALUE));
- Set<Integer> offState = new HashSet<>(
- Arrays.asList(VibratorStateChanged.State.OFF_VALUE));
-
- // Add state sets to the list in order.
- List<Set<Integer>> stateSet = Arrays.asList(onState, offState);
-
- createAndUploadConfig(atomTag, true); // True: uses attribution.
- Thread.sleep(WAIT_TIME_SHORT);
-
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", name);
-
- Thread.sleep(WAIT_TIME_LONG);
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
-
- assertStatesOccurred(stateSet, data, 300,
- atom -> atom.getVibratorStateChanged().getState().getNumber());
- }
-
- public void testWakelockState() throws Exception {
- final int atomTag = Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER;
- Set<Integer> wakelockOn = new HashSet<>(Arrays.asList(
- WakelockStateChanged.State.ACQUIRE_VALUE,
- WakelockStateChanged.State.CHANGE_ACQUIRE_VALUE));
- Set<Integer> wakelockOff = new HashSet<>(Arrays.asList(
- WakelockStateChanged.State.RELEASE_VALUE,
- WakelockStateChanged.State.CHANGE_RELEASE_VALUE));
-
- final String EXPECTED_TAG = "StatsdPartialWakelock";
- final WakeLockLevelEnum EXPECTED_LEVEL = WakeLockLevelEnum.PARTIAL_WAKE_LOCK;
-
- // Add state sets to the list in order.
- List<Set<Integer>> stateSet = Arrays.asList(wakelockOn, wakelockOff);
-
- createAndUploadConfig(atomTag, true); // True: uses attribution.
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testWakelockState");
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
-
- // Assert that the events happened in the expected order.
- assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
- atom -> atom.getWakelockStateChanged().getState().getNumber());
-
- for (EventMetricData event: data) {
- String tag = event.getAtom().getWakelockStateChanged().getTag();
- WakeLockLevelEnum type = event.getAtom().getWakelockStateChanged().getType();
- assertThat(tag).isEqualTo(EXPECTED_TAG);
- assertThat(type).isEqualTo(EXPECTED_LEVEL);
- }
- }
-
- public void testWakeupAlarm() throws Exception {
- // For automotive, all wakeup alarm becomes normal alarm. So this
- // test does not work.
- if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
- final int atomTag = Atom.WAKEUP_ALARM_OCCURRED_FIELD_NUMBER;
-
- StatsdConfig.Builder config = createConfigBuilder();
- addAtomEvent(config, atomTag, true); // True: uses attribution.
-
- List<EventMetricData> data = doDeviceMethod("testWakeupAlarm", config);
- assertThat(data.size()).isAtLeast(1);
- for (int i = 0; i < data.size(); i++) {
- WakeupAlarmOccurred wao = data.get(i).getAtom().getWakeupAlarmOccurred();
- assertThat(wao.getTag()).isEqualTo("*walarm*:android.cts.statsd.testWakeupAlarm");
- assertThat(wao.getPackageName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
- }
- }
-
- public void testWifiLockHighPerf() throws Exception {
- if (!hasFeature(FEATURE_WIFI, true)) return;
- if (!hasFeature(FEATURE_PC, false)) return;
-
- final int atomTag = Atom.WIFI_LOCK_STATE_CHANGED_FIELD_NUMBER;
- Set<Integer> lockOn = new HashSet<>(Arrays.asList(WifiLockStateChanged.State.ON_VALUE));
- Set<Integer> lockOff = new HashSet<>(Arrays.asList(WifiLockStateChanged.State.OFF_VALUE));
-
- // Add state sets to the list in order.
- List<Set<Integer>> stateSet = Arrays.asList(lockOn, lockOff);
-
- createAndUploadConfig(atomTag, true); // True: uses attribution.
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testWifiLockHighPerf");
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
-
- // Assert that the events happened in the expected order.
- assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
- atom -> atom.getWifiLockStateChanged().getState().getNumber());
-
- for (EventMetricData event : data) {
- assertThat(event.getAtom().getWifiLockStateChanged().getMode())
- .isEqualTo(WifiModeEnum.WIFI_MODE_FULL_HIGH_PERF);
- }
- }
-
- public void testWifiLockLowLatency() throws Exception {
- if (!hasFeature(FEATURE_WIFI, true)) return;
- if (!hasFeature(FEATURE_PC, false)) return;
-
- final int atomTag = Atom.WIFI_LOCK_STATE_CHANGED_FIELD_NUMBER;
- Set<Integer> lockOn = new HashSet<>(Arrays.asList(WifiLockStateChanged.State.ON_VALUE));
- Set<Integer> lockOff = new HashSet<>(Arrays.asList(WifiLockStateChanged.State.OFF_VALUE));
-
- // Add state sets to the list in order.
- List<Set<Integer>> stateSet = Arrays.asList(lockOn, lockOff);
-
- createAndUploadConfig(atomTag, true); // True: uses attribution.
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testWifiLockLowLatency");
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
-
- // Assert that the events happened in the expected order.
- assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
- atom -> atom.getWifiLockStateChanged().getState().getNumber());
-
- for (EventMetricData event : data) {
- assertThat(event.getAtom().getWifiLockStateChanged().getMode())
- .isEqualTo(WifiModeEnum.WIFI_MODE_FULL_LOW_LATENCY);
- }
- }
-
- public void testWifiMulticastLock() throws Exception {
- if (!hasFeature(FEATURE_WIFI, true)) return;
- if (!hasFeature(FEATURE_PC, false)) return;
-
- final int atomTag = Atom.WIFI_MULTICAST_LOCK_STATE_CHANGED_FIELD_NUMBER;
- Set<Integer> lockOn = new HashSet<>(
- Arrays.asList(WifiMulticastLockStateChanged.State.ON_VALUE));
- Set<Integer> lockOff = new HashSet<>(
- Arrays.asList(WifiMulticastLockStateChanged.State.OFF_VALUE));
-
- final String EXPECTED_TAG = "StatsdCTSMulticastLock";
-
- // Add state sets to the list in order.
- List<Set<Integer>> stateSet = Arrays.asList(lockOn, lockOff);
-
- createAndUploadConfig(atomTag, true);
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testWifiMulticastLock");
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
-
- // Assert that the events happened in the expected order.
- assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
- atom -> atom.getWifiMulticastLockStateChanged().getState().getNumber());
-
- for (EventMetricData event: data) {
- String tag = event.getAtom().getWifiMulticastLockStateChanged().getTag();
- assertThat(tag).isEqualTo(EXPECTED_TAG);
- }
- }
-
- public void testWifiScan() throws Exception {
- if (!hasFeature(FEATURE_WIFI, true)) return;
-
- final int atom = Atom.WIFI_SCAN_STATE_CHANGED_FIELD_NUMBER;
- final int key = WifiScanStateChanged.STATE_FIELD_NUMBER;
- final int stateOn = WifiScanStateChanged.State.ON_VALUE;
- final int stateOff = WifiScanStateChanged.State.OFF_VALUE;
- final int minTimeDiffMillis = 250;
- final int maxTimeDiffMillis = 60_000;
- final boolean demandExactlyTwo = false; // Two scans are performed, so up to 4 atoms logged.
-
- List<EventMetricData> data = doDeviceMethodOnOff("testWifiScan", atom, key,
- stateOn, stateOff, minTimeDiffMillis, maxTimeDiffMillis, demandExactlyTwo);
-
- assertThat(data.size()).isIn(Range.closed(2, 4));
- WifiScanStateChanged a0 = data.get(0).getAtom().getWifiScanStateChanged();
- WifiScanStateChanged a1 = data.get(1).getAtom().getWifiScanStateChanged();
- assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
- assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
- }
-
- public void testBinderStats() throws Exception {
- try {
- unplugDevice();
- Thread.sleep(WAIT_TIME_SHORT);
- enableBinderStats();
- binderStatsNoSampling();
- resetBinderStats();
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.BINDER_CALLS_FIELD_NUMBER, null);
-
- uploadConfig(config);
- Thread.sleep(WAIT_TIME_SHORT);
-
- runActivity("StatsdCtsForegroundActivity", "action", "action.show_notification",3_000);
-
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_SHORT);
-
- boolean found = false;
- int uid = getUid();
- List<Atom> atomList = getGaugeMetricDataList();
- for (Atom atom : atomList) {
- BinderCalls calls = atom.getBinderCalls();
- boolean classMatches = calls.getServiceClassName().contains(
- "com.android.server.notification.NotificationManagerService");
- boolean methodMatches = calls.getServiceMethodName()
- .equals("createNotificationChannels");
-
- if (calls.getUid() == uid && classMatches && methodMatches) {
- found = true;
- assertThat(calls.getRecordedCallCount()).isGreaterThan(0L);
- assertThat(calls.getCallCount()).isGreaterThan(0L);
- assertThat(calls.getRecordedTotalLatencyMicros())
- .isIn(Range.open(0L, 1000000L));
- assertThat(calls.getRecordedTotalCpuMicros()).isIn(Range.open(0L, 1000000L));
- }
- }
-
- assertWithMessage(String.format("Did not find a matching atom for uid %d", uid))
- .that(found).isTrue();
-
- } finally {
- disableBinderStats();
- plugInAc();
- }
- }
-
- public void testLooperStats() throws Exception {
- try {
- unplugDevice();
- setUpLooperStats();
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.LOOPER_STATS_FIELD_NUMBER, null);
- uploadConfig(config);
- Thread.sleep(WAIT_TIME_SHORT);
-
- runActivity("StatsdCtsForegroundActivity", "action", "action.show_notification", 3_000);
-
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_SHORT);
-
- List<Atom> atomList = getGaugeMetricDataList();
-
- boolean found = false;
- int uid = getUid();
- for (Atom atom : atomList) {
- LooperStats stats = atom.getLooperStats();
- String notificationServiceFullName =
- "com.android.server.notification.NotificationManagerService";
- boolean handlerMatches =
- stats.getHandlerClassName().equals(
- notificationServiceFullName + "$WorkerHandler");
- boolean messageMatches =
- stats.getMessageName().equals(
- notificationServiceFullName + "$EnqueueNotificationRunnable");
- if (atom.getLooperStats().getUid() == uid && handlerMatches && messageMatches) {
- found = true;
- assertThat(stats.getMessageCount()).isGreaterThan(0L);
- assertThat(stats.getRecordedMessageCount()).isGreaterThan(0L);
- assertThat(stats.getRecordedTotalLatencyMicros())
- .isIn(Range.open(0L, 1000000L));
- assertThat(stats.getRecordedTotalCpuMicros()).isIn(Range.open(0L, 1000000L));
- assertThat(stats.getRecordedMaxLatencyMicros()).isIn(Range.open(0L, 1000000L));
- assertThat(stats.getRecordedMaxCpuMicros()).isIn(Range.open(0L, 1000000L));
- assertThat(stats.getRecordedDelayMessageCount()).isGreaterThan(0L);
- assertThat(stats.getRecordedTotalDelayMillis())
- .isIn(Range.closedOpen(0L, 5000L));
- assertThat(stats.getRecordedMaxDelayMillis()).isIn(Range.closedOpen(0L, 5000L));
- }
- }
- assertWithMessage(String.format("Did not find a matching atom for uid %d", uid))
- .that(found).isTrue();
- } finally {
- cleanUpLooperStats();
- plugInAc();
- }
- }
-
- public void testProcessMemoryState() throws Exception {
- // Get ProcessMemoryState as a simple gauge metric.
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.PROCESS_MEMORY_STATE_FIELD_NUMBER, null);
- uploadConfig(config);
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Start test app.
- try (AutoCloseable a = withActivity("StatsdCtsForegroundActivity", "action",
- "action.show_notification")) {
- Thread.sleep(WAIT_TIME_LONG);
- // Trigger a pull and wait for new pull before killing the process.
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_LONG);
- }
-
- // Assert about ProcessMemoryState for the test app.
- List<Atom> atoms = getGaugeMetricDataList();
- int uid = getUid();
- boolean found = false;
- for (Atom atom : atoms) {
- ProcessMemoryState state = atom.getProcessMemoryState();
- if (state.getUid() != uid) {
- continue;
- }
- found = true;
- assertThat(state.getProcessName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
- assertThat(state.getOomAdjScore()).isAtLeast(0);
- assertThat(state.getPageFault()).isAtLeast(0L);
- assertThat(state.getPageMajorFault()).isAtLeast(0L);
- assertThat(state.getRssInBytes()).isGreaterThan(0L);
- assertThat(state.getCacheInBytes()).isAtLeast(0L);
- assertThat(state.getSwapInBytes()).isAtLeast(0L);
- }
- assertWithMessage(String.format("Did not find a matching atom for uid %d", uid))
- .that(found).isTrue();
- }
-
- public void testProcessMemoryHighWaterMark() throws Exception {
- // Get ProcessMemoryHighWaterMark as a simple gauge metric.
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.PROCESS_MEMORY_HIGH_WATER_MARK_FIELD_NUMBER, null);
- uploadConfig(config);
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Start test app and trigger a pull while it is running.
- try (AutoCloseable a = withActivity("StatsdCtsForegroundActivity", "action",
- "action.show_notification")) {
- Thread.sleep(WAIT_TIME_SHORT);
- // Trigger a pull and wait for new pull before killing the process.
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_LONG);
- }
-
- // Assert about ProcessMemoryHighWaterMark for the test app, statsd and system server.
- List<Atom> atoms = getGaugeMetricDataList();
- int uid = getUid();
- boolean foundTestApp = false;
- boolean foundStatsd = false;
- boolean foundSystemServer = false;
- for (Atom atom : atoms) {
- ProcessMemoryHighWaterMark state = atom.getProcessMemoryHighWaterMark();
- if (state.getUid() == uid) {
- foundTestApp = true;
- assertThat(state.getProcessName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
- assertThat(state.getRssHighWaterMarkInBytes()).isGreaterThan(0L);
- } else if (state.getProcessName().contains("/statsd")) {
- foundStatsd = true;
- assertThat(state.getRssHighWaterMarkInBytes()).isGreaterThan(0L);
- } else if (state.getProcessName().equals("system")) {
- foundSystemServer = true;
- assertThat(state.getRssHighWaterMarkInBytes()).isGreaterThan(0L);
- }
- }
- assertWithMessage(String.format("Did not find a matching atom for test app uid=%d",uid))
- .that(foundTestApp).isTrue();
- assertWithMessage("Did not find a matching atom for statsd").that(foundStatsd).isTrue();
- assertWithMessage("Did not find a matching atom for system server")
- .that(foundSystemServer).isTrue();
- }
-
- public void testProcessMemorySnapshot() throws Exception {
- // Get ProcessMemorySnapshot as a simple gauge metric.
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.PROCESS_MEMORY_SNAPSHOT_FIELD_NUMBER, null);
- uploadConfig(config);
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Start test app and trigger a pull while it is running.
- try (AutoCloseable a = withActivity("StatsdCtsForegroundActivity", "action",
- "action.show_notification")) {
- Thread.sleep(WAIT_TIME_LONG);
- setAppBreadcrumbPredicate();
- }
-
- // Assert about ProcessMemorySnapshot for the test app, statsd and system server.
- List<Atom> atoms = getGaugeMetricDataList();
- int uid = getUid();
- boolean foundTestApp = false;
- boolean foundStatsd = false;
- boolean foundSystemServer = false;
- for (Atom atom : atoms) {
- ProcessMemorySnapshot snapshot = atom.getProcessMemorySnapshot();
- if (snapshot.getUid() == uid) {
- foundTestApp = true;
- assertThat(snapshot.getProcessName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
- } else if (snapshot.getProcessName().contains("/statsd")) {
- foundStatsd = true;
- } else if (snapshot.getProcessName().equals("system")) {
- foundSystemServer = true;
- }
-
- assertThat(snapshot.getPid()).isGreaterThan(0);
- assertThat(snapshot.getAnonRssAndSwapInKilobytes()).isAtLeast(0);
- assertThat(snapshot.getAnonRssAndSwapInKilobytes()).isEqualTo(
- snapshot.getAnonRssInKilobytes() + snapshot.getSwapInKilobytes());
- assertThat(snapshot.getRssInKilobytes()).isAtLeast(0);
- assertThat(snapshot.getAnonRssInKilobytes()).isAtLeast(0);
- assertThat(snapshot.getSwapInKilobytes()).isAtLeast(0);
- }
- assertWithMessage(String.format("Did not find a matching atom for test app uid=%d",uid))
- .that(foundTestApp).isTrue();
- assertWithMessage("Did not find a matching atom for statsd").that(foundStatsd).isTrue();
- assertWithMessage("Did not find a matching atom for system server")
- .that(foundSystemServer).isTrue();
- }
-
- public void testIonHeapSize_optional() throws Exception {
- if (isIonHeapSizeMandatory()) {
- return;
- }
-
- List<Atom> atoms = pullIonHeapSizeAsGaugeMetric();
- if (atoms.isEmpty()) {
- // No support.
- return;
- }
- assertIonHeapSize(atoms);
- }
-
- public void testIonHeapSize_mandatory() throws Exception {
- if (!isIonHeapSizeMandatory()) {
- return;
- }
-
- List<Atom> atoms = pullIonHeapSizeAsGaugeMetric();
- assertIonHeapSize(atoms);
- }
-
- /** Returns whether IonHeapSize atom is supported. */
- private boolean isIonHeapSizeMandatory() throws Exception {
- // Support is guaranteed by libmeminfo VTS.
- return PropertyUtil.getFirstApiLevel(getDevice()) >= 30;
- }
-
- /** Returns IonHeapSize atoms pulled as a simple gauge metric while test app is running. */
- private List<Atom> pullIonHeapSizeAsGaugeMetric() throws Exception {
- // Get IonHeapSize as a simple gauge metric.
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.ION_HEAP_SIZE_FIELD_NUMBER, null);
- uploadConfig(config);
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Start test app and trigger a pull while it is running.
- try (AutoCloseable a = withActivity("StatsdCtsForegroundActivity", "action",
- "action.show_notification")) {
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_LONG);
- }
-
- return getGaugeMetricDataList();
- }
-
- private static void assertIonHeapSize(List<Atom> atoms) {
- assertThat(atoms).hasSize(1);
- IonHeapSize ionHeapSize = atoms.get(0).getIonHeapSize();
- assertThat(ionHeapSize.getTotalSizeKb()).isAtLeast(0);
- }
-
- /**
- * The the app id from a uid.
- *
- * @param uid The uid of the app
- *
- * @return The app id of the app
- *
- * @see android.os.UserHandle#getAppId
- */
- private static int getAppId(int uid) {
- return uid % 100000;
- }
-
- public void testRoleHolder() throws Exception {
- // Make device side test package a role holder
- String callScreenAppRole = "android.app.role.CALL_SCREENING";
- getDevice().executeShellCommand(
- "cmd role add-role-holder " + callScreenAppRole + " " + DEVICE_SIDE_TEST_PACKAGE);
-
- // Set up what to collect
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.ROLE_HOLDER_FIELD_NUMBER, null);
- uploadConfig(config);
- Thread.sleep(WAIT_TIME_SHORT);
-
- boolean verifiedKnowRoleState = false;
-
- // Pull a report
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_SHORT);
-
- int testAppId = getAppId(getUid());
-
- for (Atom atom : getGaugeMetricDataList()) {
- AtomsProto.RoleHolder roleHolder = atom.getRoleHolder();
-
- assertThat(roleHolder.getPackageName()).isNotNull();
- assertThat(roleHolder.getUid()).isAtLeast(0);
- assertThat(roleHolder.getRole()).isNotNull();
-
- if (roleHolder.getPackageName().equals(DEVICE_SIDE_TEST_PACKAGE)) {
- assertThat(getAppId(roleHolder.getUid())).isEqualTo(testAppId);
- assertThat(roleHolder.getPackageName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
- assertThat(roleHolder.getRole()).isEqualTo(callScreenAppRole);
-
- verifiedKnowRoleState = true;
- }
- }
-
- assertThat(verifiedKnowRoleState).isTrue();
- }
-
- public void testDangerousPermissionState() throws Exception {
- final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED = 1 << 8;
- final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED = 1 << 9;
-
- // Set up what to collect
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.DANGEROUS_PERMISSION_STATE_FIELD_NUMBER, null);
- uploadConfig(config);
- Thread.sleep(WAIT_TIME_SHORT);
-
- boolean verifiedKnowPermissionState = false;
-
- // Pull a report
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_SHORT);
-
- int testAppId = getAppId(getUid());
-
- for (Atom atom : getGaugeMetricDataList()) {
- DangerousPermissionState permissionState = atom.getDangerousPermissionState();
-
- assertThat(permissionState.getPermissionName()).isNotNull();
- assertThat(permissionState.getUid()).isAtLeast(0);
- assertThat(permissionState.getPackageName()).isNotNull();
-
- if (getAppId(permissionState.getUid()) == testAppId) {
-
- if (permissionState.getPermissionName().contains(
- "ACCESS_FINE_LOCATION")) {
- assertThat(permissionState.getIsGranted()).isTrue();
- assertThat(permissionState.getPermissionFlags() & ~(
- FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED
- | FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED))
- .isEqualTo(0);
-
- verifiedKnowPermissionState = true;
- }
- }
- }
-
- assertThat(verifiedKnowPermissionState).isTrue();
- }
-
- public void testDangerousPermissionStateSampled() throws Exception {
- // get full atom for reference
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.DANGEROUS_PERMISSION_STATE_FIELD_NUMBER, null);
- uploadConfig(config);
- Thread.sleep(WAIT_TIME_SHORT);
-
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_SHORT);
-
- List<DangerousPermissionState> fullDangerousPermissionState = new ArrayList<>();
- for (Atom atom : getGaugeMetricDataList()) {
- fullDangerousPermissionState.add(atom.getDangerousPermissionState());
- }
-
- removeConfig(CONFIG_ID);
- getReportList(); // Clears data.
- List<Atom> gaugeMetricDataList = null;
-
- // retries in case sampling returns full list or empty list - which should be extremely rare
- for (int attempt = 0; attempt < 10; attempt++) {
- // Set up what to collect
- config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.DANGEROUS_PERMISSION_STATE_SAMPLED_FIELD_NUMBER,
- null);
- uploadConfig(config);
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Pull a report
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_SHORT);
-
- gaugeMetricDataList = getGaugeMetricDataList();
- if (gaugeMetricDataList.size() > 0
- && gaugeMetricDataList.size() < fullDangerousPermissionState.size()) {
- break;
- }
- removeConfig(CONFIG_ID);
- getReportList(); // Clears data.
- }
- assertThat(gaugeMetricDataList.size()).isGreaterThan(0);
- assertThat(gaugeMetricDataList.size()).isLessThan(fullDangerousPermissionState.size());
-
- long lastUid = -1;
- int fullIndex = 0;
-
- for (Atom atom : getGaugeMetricDataList()) {
- DangerousPermissionStateSampled permissionState =
- atom.getDangerousPermissionStateSampled();
-
- DangerousPermissionState referenceState = fullDangerousPermissionState.get(fullIndex);
-
- if (referenceState.getUid() != permissionState.getUid()) {
- // atoms are sampled on uid basis if uid is present, all related permissions must
- // be logged.
- assertThat(permissionState.getUid()).isNotEqualTo(lastUid);
- continue;
- }
-
- lastUid = permissionState.getUid();
-
- assertThat(permissionState.getPermissionFlags()).isEqualTo(
- referenceState.getPermissionFlags());
- assertThat(permissionState.getIsGranted()).isEqualTo(referenceState.getIsGranted());
- assertThat(permissionState.getPermissionName()).isEqualTo(
- referenceState.getPermissionName());
-
- fullIndex++;
- }
- }
-
- public void testAppOps() throws Exception {
- // Set up what to collect
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.APP_OPS_FIELD_NUMBER, null);
- uploadConfig(config);
-
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testAppOps");
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Pull a report
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_SHORT);
-
- ArrayList<Integer> expectedOps = new ArrayList<>();
- for (int i = 0; i < NUM_APP_OPS; i++) {
- expectedOps.add(i);
- }
-
- for (Descriptors.EnumValueDescriptor valueDescriptor :
- AttributedAppOps.getDefaultInstance().getOp().getDescriptorForType().getValues()) {
- if (valueDescriptor.getOptions().hasDeprecated()) {
- // Deprecated app op, remove from list of expected ones.
- expectedOps.remove(expectedOps.indexOf(valueDescriptor.getNumber()));
- }
- }
- for (Atom atom : getGaugeMetricDataList()) {
-
- AppOps appOps = atom.getAppOps();
- if (appOps.getPackageName().equals(TEST_PACKAGE_NAME)) {
- if (appOps.getOpId().getNumber() == -1) {
- continue;
- }
- long totalNoted = appOps.getTrustedForegroundGrantedCount()
- + appOps.getTrustedBackgroundGrantedCount()
- + appOps.getTrustedForegroundRejectedCount()
- + appOps.getTrustedBackgroundRejectedCount();
- assertWithMessage("Operation in APP_OPS_ENUM_MAP: " + appOps.getOpId().getNumber())
- .that(totalNoted - 1).isEqualTo(appOps.getOpId().getNumber());
- assertWithMessage("Unexpected Op reported").that(expectedOps).contains(
- appOps.getOpId().getNumber());
- expectedOps.remove(expectedOps.indexOf(appOps.getOpId().getNumber()));
- }
- }
- assertWithMessage("Logging app op ids are missing in report.").that(expectedOps).isEmpty();
- }
-
- public void testANROccurred() throws Exception {
- final int atomTag = Atom.ANR_OCCURRED_FIELD_NUMBER;
- createAndUploadConfig(atomTag, false);
- Thread.sleep(WAIT_TIME_SHORT);
-
- try (AutoCloseable a = withActivity("ANRActivity", null, null)) {
- Thread.sleep(WAIT_TIME_SHORT);
- getDevice().executeShellCommand(
- "am broadcast -a action_anr -p " + DEVICE_SIDE_TEST_PACKAGE);
- Thread.sleep(20_000);
- }
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
-
- assertThat(data).hasSize(1);
- assertThat(data.get(0).getAtom().hasAnrOccurred()).isTrue();
- ANROccurred atom = data.get(0).getAtom().getAnrOccurred();
- assertThat(atom.getIsInstantApp().getNumber())
- .isEqualTo(ANROccurred.InstantApp.FALSE_VALUE);
- assertThat(atom.getForegroundState().getNumber())
- .isEqualTo(ANROccurred.ForegroundState.FOREGROUND_VALUE);
- assertThat(atom.getErrorSource()).isEqualTo(ErrorSource.DATA_APP);
- assertThat(atom.getPackageName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
- }
-
- public void testWriteRawTestAtom() throws Exception {
- final int atomTag = Atom.TEST_ATOM_REPORTED_FIELD_NUMBER;
- createAndUploadConfig(atomTag, true);
- Thread.sleep(WAIT_TIME_SHORT);
-
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testWriteRawTestAtom");
-
- Thread.sleep(WAIT_TIME_SHORT);
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
- assertThat(data).hasSize(4);
-
- TestAtomReported atom = data.get(0).getAtom().getTestAtomReported();
- List<AttributionNode> attrChain = atom.getAttributionNodeList();
- assertThat(attrChain).hasSize(2);
- assertThat(attrChain.get(0).getUid()).isEqualTo(1234);
- assertThat(attrChain.get(0).getTag()).isEqualTo("tag1");
- assertThat(attrChain.get(1).getUid()).isEqualTo(getUid());
- assertThat(attrChain.get(1).getTag()).isEqualTo("tag2");
-
- assertThat(atom.getIntField()).isEqualTo(42);
- assertThat(atom.getLongField()).isEqualTo(Long.MAX_VALUE);
- assertThat(atom.getFloatField()).isEqualTo(3.14f);
- assertThat(atom.getStringField()).isEqualTo("This is a basic test!");
- assertThat(atom.getBooleanField()).isFalse();
- assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.ON_VALUE);
- assertThat(atom.getBytesField().getExperimentIdList())
- .containsExactly(1L, 2L, 3L).inOrder();
-
-
- atom = data.get(1).getAtom().getTestAtomReported();
- attrChain = atom.getAttributionNodeList();
- assertThat(attrChain).hasSize(2);
- assertThat(attrChain.get(0).getUid()).isEqualTo(9999);
- assertThat(attrChain.get(0).getTag()).isEqualTo("tag9999");
- assertThat(attrChain.get(1).getUid()).isEqualTo(getUid());
- assertThat(attrChain.get(1).getTag()).isEmpty();
-
- assertThat(atom.getIntField()).isEqualTo(100);
- assertThat(atom.getLongField()).isEqualTo(Long.MIN_VALUE);
- assertThat(atom.getFloatField()).isEqualTo(-2.5f);
- assertThat(atom.getStringField()).isEqualTo("Test null uid");
- assertThat(atom.getBooleanField()).isTrue();
- assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.UNKNOWN_VALUE);
- assertThat(atom.getBytesField().getExperimentIdList())
- .containsExactly(1L, 2L, 3L).inOrder();
-
- atom = data.get(2).getAtom().getTestAtomReported();
- attrChain = atom.getAttributionNodeList();
- assertThat(attrChain).hasSize(1);
- assertThat(attrChain.get(0).getUid()).isEqualTo(getUid());
- assertThat(attrChain.get(0).getTag()).isEqualTo("tag1");
-
- assertThat(atom.getIntField()).isEqualTo(-256);
- assertThat(atom.getLongField()).isEqualTo(-1234567890L);
- assertThat(atom.getFloatField()).isEqualTo(42.01f);
- assertThat(atom.getStringField()).isEqualTo("Test non chained");
- assertThat(atom.getBooleanField()).isTrue();
- assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.OFF_VALUE);
- assertThat(atom.getBytesField().getExperimentIdList())
- .containsExactly(1L, 2L, 3L).inOrder();
-
- atom = data.get(3).getAtom().getTestAtomReported();
- attrChain = atom.getAttributionNodeList();
- assertThat(attrChain).hasSize(1);
- assertThat(attrChain.get(0).getUid()).isEqualTo(getUid());
- assertThat(attrChain.get(0).getTag()).isEmpty();
-
- assertThat(atom.getIntField()).isEqualTo(0);
- assertThat(atom.getLongField()).isEqualTo(0L);
- assertThat(atom.getFloatField()).isEqualTo(0f);
- assertThat(atom.getStringField()).isEmpty();
- assertThat(atom.getBooleanField()).isTrue();
- assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.OFF_VALUE);
- assertThat(atom.getBytesField().getExperimentIdList()).isEmpty();
- }
-
- public void testNotificationPackagePreferenceExtraction() throws Exception {
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config,
- Atom.PACKAGE_NOTIFICATION_PREFERENCES_FIELD_NUMBER,
- null);
- uploadConfig(config);
- Thread.sleep(WAIT_TIME_SHORT);
- runActivity("StatsdCtsForegroundActivity", "action", "action.show_notification");
- Thread.sleep(WAIT_TIME_SHORT);
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_SHORT);
-
- List<PackageNotificationPreferences> allPreferences = new ArrayList<>();
- for (Atom atom : getGaugeMetricDataList()){
- if(atom.hasPackageNotificationPreferences()) {
- allPreferences.add(atom.getPackageNotificationPreferences());
- }
- }
- assertThat(allPreferences.size()).isGreaterThan(0);
-
- boolean foundTestPackagePreferences = false;
- int uid = getUid();
- for (PackageNotificationPreferences pref : allPreferences) {
- assertThat(pref.getUid()).isGreaterThan(0);
- assertTrue(pref.hasImportance());
- assertTrue(pref.hasVisibility());
- assertTrue(pref.hasUserLockedFields());
- if(pref.getUid() == uid){
- assertThat(pref.getImportance()).isEqualTo(-1000); //UNSPECIFIED_IMPORTANCE
- assertThat(pref.getVisibility()).isEqualTo(-1000); //UNSPECIFIED_VISIBILITY
- foundTestPackagePreferences = true;
- }
- }
- assertTrue(foundTestPackagePreferences);
- }
-
- public void testNotificationChannelPreferencesExtraction() throws Exception {
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config,
- Atom.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES_FIELD_NUMBER,
- null);
- uploadConfig(config);
- Thread.sleep(WAIT_TIME_SHORT);
- runActivity("StatsdCtsForegroundActivity", "action", "action.show_notification");
- Thread.sleep(WAIT_TIME_SHORT);
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_SHORT);
-
- List<PackageNotificationChannelPreferences> allChannelPreferences = new ArrayList<>();
- for(Atom atom : getGaugeMetricDataList()) {
- if (atom.hasPackageNotificationChannelPreferences()) {
- allChannelPreferences.add(atom.getPackageNotificationChannelPreferences());
- }
- }
- assertThat(allChannelPreferences.size()).isGreaterThan(0);
-
- boolean foundTestPackagePreferences = false;
- int uid = getUid();
- for (PackageNotificationChannelPreferences pref : allChannelPreferences) {
- assertThat(pref.getUid()).isGreaterThan(0);
- assertTrue(pref.hasChannelId());
- assertTrue(pref.hasChannelName());
- assertTrue(pref.hasDescription());
- assertTrue(pref.hasImportance());
- assertTrue(pref.hasUserLockedFields());
- assertTrue(pref.hasIsDeleted());
- if(uid == pref.getUid() && pref.getChannelId().equals("StatsdCtsChannel")) {
- assertThat(pref.getChannelName()).isEqualTo("Statsd Cts");
- assertThat(pref.getDescription()).isEqualTo("Statsd Cts Channel");
- assertThat(pref.getImportance()).isEqualTo(3); // IMPORTANCE_DEFAULT
- foundTestPackagePreferences = true;
- }
- }
- assertTrue(foundTestPackagePreferences);
- }
-
- public void testNotificationChannelGroupPreferencesExtraction() throws Exception {
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config,
- Atom.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES_FIELD_NUMBER,
- null);
- uploadConfig(config);
- Thread.sleep(WAIT_TIME_SHORT);
- runActivity("StatsdCtsForegroundActivity", "action", "action.create_channel_group");
- Thread.sleep(WAIT_TIME_SHORT);
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_SHORT);
-
- List<PackageNotificationChannelGroupPreferences> allGroupPreferences = new ArrayList<>();
- for(Atom atom : getGaugeMetricDataList()) {
- if (atom.hasPackageNotificationChannelGroupPreferences()) {
- allGroupPreferences.add(atom.getPackageNotificationChannelGroupPreferences());
- }
- }
- assertThat(allGroupPreferences.size()).isGreaterThan(0);
-
- boolean foundTestPackagePreferences = false;
- int uid = getUid();
- for(PackageNotificationChannelGroupPreferences pref : allGroupPreferences) {
- assertThat(pref.getUid()).isGreaterThan(0);
- assertTrue(pref.hasGroupId());
- assertTrue(pref.hasGroupName());
- assertTrue(pref.hasDescription());
- assertTrue(pref.hasIsBlocked());
- assertTrue(pref.hasUserLockedFields());
- if(uid == pref.getUid() && pref.getGroupId().equals("StatsdCtsGroup")) {
- assertThat(pref.getGroupName()).isEqualTo("Statsd Cts Group");
- assertThat(pref.getDescription()).isEqualTo("StatsdCtsGroup Description");
- assertThat(pref.getIsBlocked()).isFalse();
- foundTestPackagePreferences = true;
- }
- }
- assertTrue(foundTestPackagePreferences);
- }
-
- public void testNotificationReported() throws Exception {
- StatsdConfig.Builder config = getPulledConfig();
- addAtomEvent(config, Atom.NOTIFICATION_REPORTED_FIELD_NUMBER,
- Arrays.asList(createFvm(NotificationReported.PACKAGE_NAME_FIELD_NUMBER)
- .setEqString(DEVICE_SIDE_TEST_PACKAGE)));
- uploadConfig(config);
- Thread.sleep(WAIT_TIME_SHORT);
- runActivity("StatsdCtsForegroundActivity", "action", "action.show_notification");
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
- assertThat(data).hasSize(1);
- assertThat(data.get(0).getAtom().hasNotificationReported()).isTrue();
- AtomsProto.NotificationReported n = data.get(0).getAtom().getNotificationReported();
- assertThat(n.getPackageName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
- assertThat(n.getUid()).isEqualTo(getUid());
- assertThat(n.getNotificationIdHash()).isEqualTo(1); // smallHash(0x7f080001)
- assertThat(n.getChannelIdHash()).isEqualTo(SmallHash.hash("StatsdCtsChannel"));
- assertThat(n.getGroupIdHash()).isEqualTo(0);
- assertFalse(n.getIsGroupSummary());
- assertThat(n.getCategory()).isEmpty();
- assertThat(n.getStyle()).isEqualTo(0);
- assertThat(n.getNumPeople()).isEqualTo(0);
- }
-
- public void testSettingsStatsReported() throws Exception {
- // Base64 encoded proto com.android.service.nano.StringListParamProto,
- // which contains five strings 'low_power_trigger_level', 'preferred_network_mode1',
- // 'preferred_network_mode1_int', 'wfc_ims_mode','zen_mode'
- final String encoded =
- "Chdsb3dfcG93ZXJfdHJpZ2dlcl9sZXZlbAoQd2ZjX2ltc19tb2RlID0gMgoXcHJlZmVycmVkX25ldHdvcmtfbW9kZTEKG3ByZWZlcnJlZF9uZXR3b3JrX21vZGUxX2ludAoIemVuX21vZGU";
- final String network_mode1 = "preferred_network_mode1";
-
- int originalNetworkMode;
- try {
- originalNetworkMode = Integer.parseInt(
- getDevice().executeShellCommand("settings get global " + network_mode1));
- } catch (NumberFormatException e) {
- // The default value, zen mode is not enabled
- originalNetworkMode = 0;
- }
-
- // Clear settings_stats device config.
- Thread.sleep(WAIT_TIME_SHORT);
- getDevice().executeShellCommand(
- "device_config reset untrusted_clear settings_stats");
- // Set whitelist through device config.
- Thread.sleep(WAIT_TIME_SHORT);
- getDevice().executeShellCommand(
- "device_config put settings_stats GlobalFeature__integer_whitelist " + encoded);
- Thread.sleep(WAIT_TIME_SHORT);
- // Set network_mode1 value
- getDevice().executeShellCommand("settings put global " + network_mode1 + " 15");
-
- // Get SettingSnapshot as a simple gauge metric.
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config, Atom.SETTING_SNAPSHOT_FIELD_NUMBER, null);
- uploadConfig(config);
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Start test app and trigger a pull while it is running.
- try (AutoCloseable a = withActivity("StatsdCtsForegroundActivity", "action",
- "action.show_notification")) {
- Thread.sleep(WAIT_TIME_SHORT);
- // Trigger a pull and wait for new pull before killing the process.
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_LONG);
- }
-
- // Test the size of atoms. It should contain 5 atoms
- List<Atom> atoms = getGaugeMetricDataList();
- assertThat(atoms.size()).isEqualTo(5);
- SettingSnapshot snapshot = null;
- for (Atom atom : atoms) {
- SettingSnapshot settingSnapshot = atom.getSettingSnapshot();
- if (network_mode1.equals(settingSnapshot.getName())) {
- snapshot = settingSnapshot;
- break;
- }
- }
-
- Thread.sleep(WAIT_TIME_SHORT);
- // Test the data of atom.
- assertNotNull(snapshot);
- // Get setting value and test value type.
- final int newNetworkMode = Integer.parseInt(
- getDevice().executeShellCommand("settings get global " + network_mode1).trim());
- assertThat(snapshot.getType()).isEqualTo(
- SettingSnapshot.SettingsValueType.ASSIGNED_INT_TYPE);
- assertThat(snapshot.getBoolValue()).isEqualTo(false);
- assertThat(snapshot.getIntValue()).isEqualTo(newNetworkMode);
- assertThat(snapshot.getFloatValue()).isEqualTo(0f);
- assertThat(snapshot.getStrValue()).isEqualTo("");
- assertThat(snapshot.getUserId()).isEqualTo(0);
-
- // Restore the setting value.
- getDevice().executeShellCommand(
- "settings put global " + network_mode1 + " " + originalNetworkMode);
- }
-
- public void testIntegrityCheckAtomReportedDuringInstall() throws Exception {
- createAndUploadConfig(AtomsProto.Atom.INTEGRITY_CHECK_RESULT_REPORTED_FIELD_NUMBER);
-
- getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
- installTestApp();
-
- List<EventMetricData> data = getEventMetricDataList();
-
- assertThat(data.size()).isEqualTo(1);
- assertThat(data.get(0).getAtom().hasIntegrityCheckResultReported()).isTrue();
- IntegrityCheckResultReported result = data.get(0)
- .getAtom().getIntegrityCheckResultReported();
- assertThat(result.getPackageName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
- // we do not assert on certificates since it seem to differ by device.
- assertThat(result.getInstallerPackageName()).isEqualTo("adb");
- assertThat(result.getVersionCode()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE_VERSION);
- assertThat(result.getResponse()).isEqualTo(ALLOWED);
- assertThat(result.getCausedByAppCertRule()).isFalse();
- assertThat(result.getCausedByInstallerRule()).isFalse();
- }
-
- public void testMobileBytesTransfer() throws Throwable {
- final int appUid = getUid();
-
- // Verify MobileBytesTransfer, passing a ThrowingPredicate that verifies contents of
- // corresponding atom type to prevent code duplication. The passed predicate returns
- // true if the atom of appUid is found, false otherwise, and throws an exception if
- // contents are not expected.
- doTestMobileBytesTransferThat(Atom.MOBILE_BYTES_TRANSFER_FIELD_NUMBER, (atom) -> {
- final AtomsProto.MobileBytesTransfer data = ((Atom) atom).getMobileBytesTransfer();
- if (data.getUid() == appUid) {
- assertDataUsageAtomDataExpected(data.getRxBytes(), data.getTxBytes(),
- data.getRxPackets(), data.getTxPackets());
- return true; // found
- }
- return false;
- });
- }
-
- public void testMobileBytesTransferByFgBg() throws Throwable {
- final int appUid = getUid();
-
- doTestMobileBytesTransferThat(Atom.MOBILE_BYTES_TRANSFER_BY_FG_BG_FIELD_NUMBER, (atom) -> {
- final AtomsProto.MobileBytesTransferByFgBg data =
- ((Atom) atom).getMobileBytesTransferByFgBg();
- if (data.getUid() == appUid && data.getIsForeground()) {
- assertDataUsageAtomDataExpected(data.getRxBytes(), data.getTxBytes(),
- data.getRxPackets(), data.getTxPackets());
- return true; // found
- }
- return false;
- });
- }
-
- public void testDataUsageBytesTransfer() throws Throwable {
- final boolean subtypeCombined = getNetworkStatsCombinedSubTypeEnabled();
-
- doTestMobileBytesTransferThat(Atom.DATA_USAGE_BYTES_TRANSFER_FIELD_NUMBER, (atom) -> {
- final AtomsProto.DataUsageBytesTransfer data =
- ((Atom) atom).getDataUsageBytesTransfer();
- if (data.getState() == 1 /*NetworkStats.SET_FOREGROUND*/) {
- assertDataUsageAtomDataExpected(data.getRxBytes(), data.getTxBytes(),
- data.getRxPackets(), data.getTxPackets());
- // TODO: verify the RAT type field with the value gotten from device.
- if (subtypeCombined) {
- assertThat(data.getRatType()).isEqualTo(
- NetworkTypeEnum.NETWORK_TYPE_UNKNOWN_VALUE);
- } else {
- assertThat(data.getRatType()).isGreaterThan(
- NetworkTypeEnum.NETWORK_TYPE_UNKNOWN_VALUE);
- }
-
- // Assert that subscription info is valid.
- assertThat(data.getSimMcc()).matches("^\\d{3}$");
- assertThat(data.getSimMnc()).matches("^\\d{2,3}$");
- assertThat(data.getCarrierId()).isNotEqualTo(
- -1); // TelephonyManager#UNKNOWN_CARRIER_ID
-
- return true; // found
- }
- return false;
- });
- }
-
- // TODO(b/157651730): Determine how to test tag and metered state within atom.
- public void testBytesTransferByTagAndMetered() throws Throwable {
- final int appUid = getUid();
- final int atomId = Atom.BYTES_TRANSFER_BY_TAG_AND_METERED_FIELD_NUMBER;
-
- doTestMobileBytesTransferThat(atomId, (atom) -> {
- final AtomsProto.BytesTransferByTagAndMetered data =
- ((Atom) atom).getBytesTransferByTagAndMetered();
- if (data.getUid() == appUid && data.getTag() == 0 /*app traffic generated on tag 0*/) {
- assertDataUsageAtomDataExpected(data.getRxBytes(), data.getTxBytes(),
- data.getRxPackets(), data.getTxPackets());
- return true; // found
- }
- return false;
- });
- }
-
- public void testIsolatedToHostUidMapping() throws Exception {
- createAndUploadConfig(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER, /*useAttribution=*/false);
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Create an isolated service from which An AppBreadcrumbReported atom is written.
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testIsolatedProcessService");
-
- List<EventMetricData> data = getEventMetricDataList();
- assertThat(data).hasSize(1);
- AppBreadcrumbReported atom = data.get(0).getAtom().getAppBreadcrumbReported();
- assertThat(atom.getUid()).isEqualTo(getUid());
- assertThat(atom.getLabel()).isEqualTo(0);
- assertThat(atom.getState()).isEqualTo(AppBreadcrumbReported.State.START);
- }
-
- public void testPushedBlobStoreStats() throws Exception {
- StatsdConfig.Builder conf = createConfigBuilder();
- addAtomEvent(conf, Atom.BLOB_COMMITTED_FIELD_NUMBER, false);
- addAtomEvent(conf, Atom.BLOB_LEASED_FIELD_NUMBER, false);
- addAtomEvent(conf, Atom.BLOB_OPENED_FIELD_NUMBER, false);
- uploadConfig(conf);
-
- Thread.sleep(WAIT_TIME_SHORT);
-
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testBlobStore");
-
- List<EventMetricData> data = getEventMetricDataList();
- assertThat(data).hasSize(3);
-
- BlobCommitted blobCommitted = data.get(0).getAtom().getBlobCommitted();
- final long blobId = blobCommitted.getBlobId();
- final long blobSize = blobCommitted.getSize();
- assertThat(blobCommitted.getUid()).isEqualTo(getUid());
- assertThat(blobId).isNotEqualTo(0);
- assertThat(blobSize).isNotEqualTo(0);
- assertThat(blobCommitted.getResult()).isEqualTo(BlobCommitted.Result.SUCCESS);
-
- BlobLeased blobLeased = data.get(1).getAtom().getBlobLeased();
- assertThat(blobLeased.getUid()).isEqualTo(getUid());
- assertThat(blobLeased.getBlobId()).isEqualTo(blobId);
- assertThat(blobLeased.getSize()).isEqualTo(blobSize);
- assertThat(blobLeased.getResult()).isEqualTo(BlobLeased.Result.SUCCESS);
-
- BlobOpened blobOpened = data.get(2).getAtom().getBlobOpened();
- assertThat(blobOpened.getUid()).isEqualTo(getUid());
- assertThat(blobOpened.getBlobId()).isEqualTo(blobId);
- assertThat(blobOpened.getSize()).isEqualTo(blobSize);
- assertThat(blobOpened.getResult()).isEqualTo(BlobOpened.Result.SUCCESS);
- }
-
- // Constants that match the constants for AtomTests#testBlobStore
- private static final long BLOB_COMMIT_CALLBACK_TIMEOUT_SEC = 5;
- private static final long BLOB_EXPIRY_DURATION_MS = 24 * 60 * 60 * 1000;
- private static final long BLOB_FILE_SIZE_BYTES = 23 * 1024L;
- private static final long BLOB_LEASE_EXPIRY_DURATION_MS = 60 * 60 * 1000;
-
- public void testPulledBlobStoreStats() throws Exception {
- StatsdConfig.Builder config = createConfigBuilder();
- addGaugeAtomWithDimensions(config,
- Atom.BLOB_INFO_FIELD_NUMBER,
- null);
- uploadConfig(config);
-
- final long testStartTimeMs = System.currentTimeMillis();
- Thread.sleep(WAIT_TIME_SHORT);
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testBlobStore");
- Thread.sleep(WAIT_TIME_LONG);
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Add commit callback time to test end time to account for async execution
- final long testEndTimeMs =
- System.currentTimeMillis() + BLOB_COMMIT_CALLBACK_TIMEOUT_SEC * 1000;
-
- // Find the BlobInfo for the blob created in the test run
- AtomsProto.BlobInfo blobInfo = null;
- for (Atom atom : getGaugeMetricDataList()) {
- if (atom.hasBlobInfo()) {
- final AtomsProto.BlobInfo temp = atom.getBlobInfo();
- if (temp.getCommitters().getCommitter(0).getUid() == getUid()) {
- blobInfo = temp;
- break;
- }
- }
- }
- assertThat(blobInfo).isNotNull();
-
- assertThat(blobInfo.getSize()).isEqualTo(BLOB_FILE_SIZE_BYTES);
-
- // Check that expiry time is reasonable
- assertThat(blobInfo.getExpiryTimestampMillis()).isGreaterThan(
- testStartTimeMs + BLOB_EXPIRY_DURATION_MS);
- assertThat(blobInfo.getExpiryTimestampMillis()).isLessThan(
- testEndTimeMs + BLOB_EXPIRY_DURATION_MS);
-
- // Check that commit time is reasonable
- final long commitTimeMs = blobInfo.getCommitters().getCommitter(
- 0).getCommitTimestampMillis();
- assertThat(commitTimeMs).isGreaterThan(testStartTimeMs);
- assertThat(commitTimeMs).isLessThan(testEndTimeMs);
-
- // Check that WHITELIST and PRIVATE access mode flags are set
- assertThat(blobInfo.getCommitters().getCommitter(0).getAccessMode()).isEqualTo(0b1001);
- assertThat(blobInfo.getCommitters().getCommitter(0).getNumWhitelistedPackage()).isEqualTo(
- 1);
-
- assertThat(blobInfo.getLeasees().getLeaseeCount()).isGreaterThan(0);
- assertThat(blobInfo.getLeasees().getLeasee(0).getUid()).isEqualTo(getUid());
-
- // Check that lease expiry time is reasonable
- final long leaseExpiryMs = blobInfo.getLeasees().getLeasee(
- 0).getLeaseExpiryTimestampMillis();
- assertThat(leaseExpiryMs).isGreaterThan(testStartTimeMs + BLOB_LEASE_EXPIRY_DURATION_MS);
- assertThat(leaseExpiryMs).isLessThan(testEndTimeMs + BLOB_LEASE_EXPIRY_DURATION_MS);
- }
-
- private void assertDataUsageAtomDataExpected(long rxb, long txb, long rxp, long txp) {
- assertThat(rxb).isGreaterThan(0L);
- assertThat(txb).isGreaterThan(0L);
- assertThat(rxp).isGreaterThan(0L);
- assertThat(txp).isGreaterThan(0L);
- }
-
- private void doTestMobileBytesTransferThat(int atomTag, ThrowingPredicate p)
- throws Throwable {
- if (!hasFeature(FEATURE_TELEPHONY, true)) return;
-
- // Get MobileBytesTransfer as a simple gauge metric.
- final StatsdConfig.Builder config = getPulledConfig();
- addGaugeAtomWithDimensions(config, atomTag, null);
- uploadConfig(config);
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Generate some traffic on mobile network.
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testGenerateMobileTraffic");
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Force polling NetworkStatsService to get most updated network stats from lower layer.
- runActivity("StatsdCtsForegroundActivity", "action", "action.poll_network_stats");
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Pull a report
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_SHORT);
-
- final List<Atom> atoms = getGaugeMetricDataList(/*checkTimestampTruncated=*/true);
- assertThat(atoms.size()).isAtLeast(1);
-
- boolean foundAppStats = false;
- for (final Atom atom : atoms) {
- if (p.accept(atom)) {
- foundAppStats = true;
- }
- }
- assertWithMessage("uid " + getUid() + " is not found in " + atoms.size() + " atoms")
- .that(foundAppStats).isTrue();
- }
-
- @FunctionalInterface
- private interface ThrowingPredicate<S, T extends Throwable> {
- boolean accept(S s) throws T;
- }
-
- public void testPackageInstallerV2MetricsReported() throws Throwable {
- if (!hasFeature(FEATURE_INCREMENTAL_DELIVERY, true)) return;
- final AtomsProto.PackageInstallerV2Reported report = installPackageUsingV2AndGetReport(
- new String[]{TEST_INSTALL_APK});
- assertTrue(report.getIsIncremental());
- // tests are ran using SHELL_UID and installation will be treated as adb install
- assertEquals("", report.getPackageName());
- assertEquals(1, report.getReturnCode());
- assertTrue(report.getDurationMillis() > 0);
- assertEquals(getTestFileSize(TEST_INSTALL_APK), report.getApksSizeBytes());
-
- getDevice().uninstallPackage(TEST_INSTALL_PACKAGE);
- }
-
- public void testPackageInstallerV2MetricsReportedForSplits() throws Throwable {
- if (!hasFeature(FEATURE_INCREMENTAL_DELIVERY, true)) return;
-
- final AtomsProto.PackageInstallerV2Reported report = installPackageUsingV2AndGetReport(
- new String[]{TEST_INSTALL_APK_BASE, TEST_INSTALL_APK_SPLIT});
- assertTrue(report.getIsIncremental());
- // tests are ran using SHELL_UID and installation will be treated as adb install
- assertEquals("", report.getPackageName());
- assertEquals(1, report.getReturnCode());
- assertTrue(report.getDurationMillis() > 0);
- assertEquals(
- getTestFileSize(TEST_INSTALL_APK_BASE) + getTestFileSize(TEST_INSTALL_APK_SPLIT),
- report.getApksSizeBytes());
-
- getDevice().uninstallPackage(TEST_INSTALL_PACKAGE);
- }
-
- public void testAppForegroundBackground() throws Exception {
- Set<Integer> onStates = new HashSet<>(Arrays.asList(
- AppUsageEventOccurred.EventType.MOVE_TO_FOREGROUND_VALUE));
- Set<Integer> offStates = new HashSet<>(Arrays.asList(
- AppUsageEventOccurred.EventType.MOVE_TO_BACKGROUND_VALUE));
-
- List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
- createAndUploadConfig(Atom.APP_USAGE_EVENT_OCCURRED_FIELD_NUMBER, false);
- Thread.sleep(WAIT_TIME_FOR_CONFIG_UPDATE_MS);
-
- getDevice().executeShellCommand(String.format(
- "am start -n '%s' -e %s %s",
- "com.android.server.cts.device.statsd/.StatsdCtsForegroundActivity",
- "action", ACTION_SHOW_APPLICATION_OVERLAY));
- final int waitTime = EXTRA_WAIT_TIME_MS + 5_000; // Overlay may need to sit there a while.
- Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS);
-
- List<EventMetricData> data = getEventMetricDataList();
- Function<Atom, Integer> appUsageStateFunction =
- atom -> atom.getAppUsageEventOccurred().getEventType().getNumber();
- popUntilFind(data, onStates, appUsageStateFunction); // clear out initial appusage states.s
- assertStatesOccurred(stateSet, data, 0, appUsageStateFunction);
- }
-
- public void testAppForceStopUsageEvent() throws Exception {
- Set<Integer> onStates = new HashSet<>(Arrays.asList(
- AppUsageEventOccurred.EventType.MOVE_TO_FOREGROUND_VALUE));
- Set<Integer> offStates = new HashSet<>(Arrays.asList(
- AppUsageEventOccurred.EventType.MOVE_TO_BACKGROUND_VALUE));
-
- List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
- createAndUploadConfig(Atom.APP_USAGE_EVENT_OCCURRED_FIELD_NUMBER, false);
- Thread.sleep(WAIT_TIME_FOR_CONFIG_UPDATE_MS);
-
- getDevice().executeShellCommand(String.format(
- "am start -n '%s' -e %s %s",
- "com.android.server.cts.device.statsd/.StatsdCtsForegroundActivity",
- "action", ACTION_LONG_SLEEP_WHILE_TOP));
- final int waitTime = EXTRA_WAIT_TIME_MS + 5_000;
- Thread.sleep(waitTime);
-
- getDevice().executeShellCommand(String.format(
- "am force-stop %s",
- "com.android.server.cts.device.statsd/.StatsdCtsForegroundActivity"));
- Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS);
-
- List<EventMetricData> data = getEventMetricDataList();
- Function<Atom, Integer> appUsageStateFunction =
- atom -> atom.getAppUsageEventOccurred().getEventType().getNumber();
- popUntilFind(data, onStates, appUsageStateFunction); // clear out initial appusage states.
- assertStatesOccurred(stateSet, data, 0, appUsageStateFunction);
- }
-
- private AtomsProto.PackageInstallerV2Reported installPackageUsingV2AndGetReport(
- String[] apkNames) throws Exception {
- createAndUploadConfig(Atom.PACKAGE_INSTALLER_V2_REPORTED_FIELD_NUMBER);
- Thread.sleep(WAIT_TIME_SHORT);
- installPackageUsingIncremental(apkNames, TEST_REMOTE_DIR);
- assertTrue(getDevice().isPackageInstalled(TEST_INSTALL_PACKAGE));
- Thread.sleep(WAIT_TIME_SHORT);
-
- List<AtomsProto.PackageInstallerV2Reported> reports = new ArrayList<>();
- for(EventMetricData data : getEventMetricDataList()) {
- if (data.getAtom().hasPackageInstallerV2Reported()) {
- reports.add(data.getAtom().getPackageInstallerV2Reported());
- }
- }
- assertEquals(1, reports.size());
- return reports.get(0);
- }
-
- private void installPackageUsingIncremental(String[] apkNames, String remoteDirPath)
- throws Exception {
- getDevice().executeShellCommand("mkdir " + remoteDirPath);
- String[] remoteApkPaths = new String[apkNames.length];
- for (int i = 0; i < remoteApkPaths.length; i++) {
- remoteApkPaths[i] = pushApkToRemote(apkNames[i], remoteDirPath);
- }
- getDevice().executeShellCommand(
- "pm install-incremental -t -g " + String.join(" ", remoteApkPaths));
- }
-
- private String pushApkToRemote(String apkName, String remoteDirPath)
- throws Exception {
- CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
- final File apk = buildHelper.getTestFile(apkName);
- final String remoteApkPath = remoteDirPath + "/" + apk.getName();
- assertTrue(getDevice().pushFile(apk, remoteApkPath));
- assertNotNull(apk);
- return remoteApkPath;
- }
-
- private long getTestFileSize(String fileName) throws Exception {
- CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
- final File file = buildHelper.getTestFile(fileName);
- return file.length();
- }
-}
diff --git a/tests/src/android/cts/statsd/validation/ValidationTests.java b/tests/src/android/cts/statsd/validation/ValidationTests.java
index 87f6840..3e2de0a 100644
--- a/tests/src/android/cts/statsd/validation/ValidationTests.java
+++ b/tests/src/android/cts/statsd/validation/ValidationTests.java
@@ -143,7 +143,6 @@
// ADB disconnection causes failure of getUid(). Move up here before turnScreenOff().
final int EXPECTED_UID = getUid();
-
turnScreenOn(); // To ensure that the ScreenOff later gets logged.
// AoD needs to be turned off because the screen should go into an off state. But, if AoD is
// on and the device doesn't support STATE_DOZE, the screen sadly goes back to STATE_ON.
@@ -190,7 +189,7 @@
long statsdDurationMs = statsdWakelockData.get(EXPECTED_UID)
.get(EXPECTED_TAG_HASH) / 1_000_000;
assertWithMessage(
- "Wakelock in statsd with uid %s and tag %s was too short or too long",
+ "Wakelock in statsd with uid %s and tag %s was too short or too long",
EXPECTED_UID, EXPECTED_TAG
).that(statsdDurationMs).isIn(Range.closed((long) MIN_DURATION, (long) MAX_DURATION));