summaryrefslogtreecommitdiff
path: root/cmds/statsd/src
diff options
context:
space:
mode:
Diffstat (limited to 'cmds/statsd/src')
-rw-r--r--cmds/statsd/src/StatsLogProcessor.h8
-rw-r--r--cmds/statsd/src/StatsService.cpp2
-rw-r--r--cmds/statsd/src/atoms.proto128
-rw-r--r--cmds/statsd/src/external/StatsPuller.cpp13
-rw-r--r--cmds/statsd/src/external/StatsPuller.h7
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.cpp23
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.h12
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.cpp6
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.h4
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.cpp11
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.h4
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.cpp2
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.h8
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.cpp7
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.h10
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.cpp19
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.h2
-rw-r--r--cmds/statsd/src/metrics/metrics_manager_util.cpp11
-rw-r--r--cmds/statsd/src/shell/ShellSubscriber.cpp3
-rw-r--r--cmds/statsd/src/state/StateListener.h4
-rw-r--r--cmds/statsd/src/state/StateTracker.cpp54
-rw-r--r--cmds/statsd/src/state/StateTracker.h6
-rw-r--r--cmds/statsd/src/statsd_config.proto4
23 files changed, 260 insertions, 88 deletions
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index ffd83ba978f4..7090bd46635d 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -307,9 +307,6 @@ private:
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation);
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition);
FRIEND_TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation);
FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket);
FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets);
@@ -328,6 +325,7 @@ private:
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
+ FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges);
FRIEND_TEST(CountMetricE2eTest, TestSlicedState);
FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap);
FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates);
@@ -345,6 +343,10 @@ private:
FRIEND_TEST(DurationMetricE2eTest, TestSlicedStatePrimaryFieldsNotSubsetDimInWhat);
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset);
+ FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation);
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState);
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions);
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index a65f5f792daa..4ffa040fafd4 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -826,7 +826,7 @@ status_t StatsService::cmd_print_pulled_metrics(int out, const Vector<String8>&
uids.push_back(AID_SYSTEM);
}
vector<shared_ptr<LogEvent>> stats;
- if (mPullerManager->Pull(s, uids, &stats)) {
+ if (mPullerManager->Pull(s, uids, getElapsedRealtimeNs(), &stats)) {
for (const auto& it : stats) {
dprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str());
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 4db0f91ca871..12158b318676 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -440,6 +440,7 @@ message Atom {
273 [(module) = "permissioncontroller"];
EvsUsageStatsReported evs_usage_stats_reported = 274 [(module) = "evs"];
AudioPowerUsageDataReported audio_power_usage_data_reported = 275;
+ TvTunerStateChanged tv_tuner_state_changed = 276 [(module) = "framework"];
SdkExtensionStatus sdk_extension_status = 354;
// StatsdStats tracks platform atoms with ids upto 500.
@@ -447,7 +448,7 @@ message Atom {
}
// Pulled events will start at field 10000.
- // Next: 10080
+ // Next: 10084
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"];
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"];
@@ -538,6 +539,10 @@ message Atom {
SimSlotState sim_slot_state = 10078 [(module) = "telephony"];
SupportedRadioAccessFamily supported_radio_access_family = 10079 [(module) = "telephony"];
SettingSnapshot setting_snapshot = 10080 [(module) = "framework"];
+ DisplayWakeReason display_wake_reason = 10081 [(module) = "framework"];
+ DataUsageBytesTransfer data_usage_bytes_transfer = 10082 [(module) = "framework"];
+ BytesTransferByTagAndMetered bytes_transfer_by_tag_and_metered =
+ 10083 [(module) = "framework"];
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -4939,6 +4944,60 @@ message MobileBytesTransferByFgBg {
}
/**
+ * Used for pull network statistics via mobile|wifi networks, and sliced by interesting dimensions.
+ * Note that the data is expected to be sliced into more dimensions in future. In other words,
+ * the caller must not assume any row of data is one full report when filtering with a set of
+ * matching conditions, because future data may represent with multiple rows what is currently
+ * represented by one.
+ * To avoid being broken by future slicing, callers must take care to aggregate rows even if they
+ * query all the existing columns.
+ *
+ * Pulled from:
+ * StatsPullAtomService (using NetworkStatsService to get NetworkStats)
+ */
+message DataUsageBytesTransfer {
+ // State of this record. Should be NetworkStats#SET_DEFAULT or NetworkStats#SET_FOREGROUND to
+ // indicate the foreground state, or NetworkStats#SET_ALL to indicate the record is for all
+ // states combined, not including debug states. See NetworkStats#SET_*.
+ optional int32 state = 1;
+
+ optional int64 rx_bytes = 2;
+
+ optional int64 rx_packets = 3;
+
+ optional int64 tx_bytes = 4;
+
+ optional int64 tx_packets = 5;
+
+ // Radio Access Technology (RAT) type of this record, should be one of
+ // TelephonyManager#NETWORK_TYPE_* constants, or NetworkTemplate#NETWORK_TYPE_ALL to indicate
+ // the record is for all rat types combined.
+ optional int32 rat_type = 6;
+
+ // Mcc/Mnc read from sim if the record is for a specific subscription, null indicates the
+ // record is combined across subscriptions.
+ optional string sim_mcc = 7;
+ optional string sim_mnc = 8;
+
+ // Allows mobile virtual network operators (MVNOs) to be identified with individual IDs.
+ // See TelephonyManager#getSimCarrierId.
+ optional int32 carrier_id = 9;
+
+ // Enumeration of opportunistic states with an additional ALL state indicates the record is
+ // combined regardless of the boolean value in its field.
+ enum DataSubscriptionState {
+ UNKNOWN = 0; // For server side backward compatibility.
+ ALL = 1;
+ OPPORTUNISTIC = 2;
+ NOT_OPPORTUNISTIC = 3;
+ }
+ // Mark whether the subscription is an opportunistic data subscription, and ALL indicates the
+ // record is combined across opportunistic data subscriptions.
+ // See {@link SubscriptionManager#setOpportunistic}.
+ optional DataSubscriptionState opportunistic_data_sub = 10;
+}
+
+/**
* Pulls bytes transferred via bluetooth. It is pulled from Bluetooth controller.
*
* Pulled from:
@@ -5981,6 +6040,12 @@ message PackageNotificationChannelPreferences {
optional int32 user_locked_fields = 6;
// Indicates if the channel was deleted by the app.
optional bool is_deleted = 7;
+ // Indicates if the channel was marked as a conversation by the app.
+ optional bool is_conversation = 8;
+ // Indicates if the channel is a conversation that was demoted by the user.
+ optional bool is_demoted_conversation = 9;
+ // Indicates if the channel is a conversation that was marked as important by the user.
+ optional bool is_important_conversation = 10;
}
/**
@@ -9120,6 +9185,28 @@ message SdkExtensionStatus {
}
/**
+ * Logs when a tune occurs through device's Frontend.
+ * This is atom ID 276.
+ *
+ * Logged from:
+ * frameworks/base/media/java/android/media/tv/tuner/Tuner.java
+ */
+message TvTunerStateChanged {
+ enum State {
+ UNKNOWN = 0;
+ TUNING = 1; // Signal is tuned
+ LOCKED = 2; // the signal is locked
+ NOT_LOCKED = 3; // the signal isn’t locked.
+ SIGNAL_LOST = 4; // the signal was locked, but is lost now.
+ SCANNING = 5; // the signal is scanned
+ SCAN_STOPPED = 6; // the scan is stopped.
+ }
+ // The uid of the application that sent this custom atom.
+ optional int32 uid = 1 [(is_uid) = true];
+ // new state
+ optional State state = 2;
+}
+/**
* Logs when an app is frozen or unfrozen.
*
* Logged from:
@@ -9461,9 +9548,6 @@ message TvSettingsUIInteracted {
/** The ID of the entry that the users actioned on */
optional android.app.tvsettings.ItemId item_id = 2;
-
- /** Additional information (e.g., navigation direction on page focused) */
- optional string additional_info = 3;
}
/**
@@ -9589,6 +9673,9 @@ message UserLifecycleEventOccurred {
SWITCH_USER = 1; // Indicates that this is a user switch event
START_USER = 2; // Indicates that this is a user start event
CREATE_USER = 3; // Indicates that this is a user create event
+ USER_RUNNING_LOCKED = 4; // Indicates that user is running in locked state
+ UNLOCKING_USER = 5; // Indicates that this is a user unlocking event
+ UNLOCKED_USER = 6; // Indicates that this is a user unlocked event
}
optional Event event = 3;
@@ -9635,6 +9722,17 @@ message AccessibilityServiceReported {
optional android.stats.accessibility.ServiceStatus service_status = 2;
}
+message DisplayWakeReason {
+ // Wake_up_reason code
+ // If LOWORD(wake_up_reason) = 0
+ // reference to HIWORD(wake_up_reason) PowerManager.WAKE_REASON_XXX
+ // else reference wake_up_reason to
+ // frameworks/base/services/core/java/com/android/server/power/Notifier.java#DispWakeupReason
+ optional int32 wake_up_reason = 1;
+ // Count of wake up by reason
+ optional int32 wake_times = 2;
+}
+
/**
* Logs app usage events.
*/
@@ -9747,3 +9845,25 @@ message AudioPowerUsageDataReported {
}
optional AudioType type = 4;
}
+
+/**
+ * Pulls bytes transferred over WiFi and mobile networks sliced by uid, is_metered, and tag.
+ *
+ * Pulled from:
+ * StatsPullAtomService, which uses NetworkStatsService to query NetworkStats.
+ */
+message BytesTransferByTagAndMetered {
+ optional int32 uid = 1 [(is_uid) = true];
+
+ optional bool is_metered = 2;
+
+ optional int32 tag = 3;
+
+ optional int64 rx_bytes = 4;
+
+ optional int64 rx_packets = 5;
+
+ optional int64 tx_bytes = 6;
+
+ optional int64 tx_packets = 7;
+}
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index 829a60345ba7..9df4d1f8ce24 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -38,14 +38,16 @@ StatsPuller::StatsPuller(const int tagId, const int64_t coolDownNs, const int64_
mPullTimeoutNs(pullTimeoutNs),
mCoolDownNs(coolDownNs),
mAdditiveFields(additiveFields),
- mLastPullTimeNs(0) {
+ mLastPullTimeNs(0),
+ mLastEventTimeNs(0) {
}
-bool StatsPuller::Pull(std::vector<std::shared_ptr<LogEvent>>* data) {
+bool StatsPuller::Pull(const int64_t eventTimeNs, std::vector<std::shared_ptr<LogEvent>>* data) {
lock_guard<std::mutex> lock(mLock);
int64_t elapsedTimeNs = getElapsedRealtimeNs();
StatsdStats::getInstance().notePull(mTagId);
- const bool shouldUseCache = elapsedTimeNs - mLastPullTimeNs < mCoolDownNs;
+ const bool shouldUseCache =
+ (mLastEventTimeNs == eventTimeNs) || (elapsedTimeNs - mLastPullTimeNs < mCoolDownNs);
if (shouldUseCache) {
if (mHasGoodData) {
(*data) = mCachedData;
@@ -54,13 +56,13 @@ bool StatsPuller::Pull(std::vector<std::shared_ptr<LogEvent>>* data) {
}
return mHasGoodData;
}
-
if (mLastPullTimeNs > 0) {
StatsdStats::getInstance().updateMinPullIntervalSec(
mTagId, (elapsedTimeNs - mLastPullTimeNs) / NS_PER_SEC);
}
mCachedData.clear();
mLastPullTimeNs = elapsedTimeNs;
+ mLastEventTimeNs = eventTimeNs;
mHasGoodData = PullInternal(&mCachedData);
if (!mHasGoodData) {
return mHasGoodData;
@@ -70,7 +72,7 @@ bool StatsPuller::Pull(std::vector<std::shared_ptr<LogEvent>>* data) {
const bool pullTimeOut = pullDurationNs > mPullTimeoutNs;
if (pullTimeOut) {
// Something went wrong. Discard the data.
- clearCacheLocked();
+ mCachedData.clear();
mHasGoodData = false;
StatsdStats::getInstance().notePullTimeout(mTagId);
ALOGW("Pull for atom %d exceeds timeout %lld nano seconds.", mTagId,
@@ -104,6 +106,7 @@ int StatsPuller::clearCacheLocked() {
int ret = mCachedData.size();
mCachedData.clear();
mLastPullTimeNs = 0;
+ mLastEventTimeNs = 0;
return ret;
}
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index fee571c52a5f..470d15e6fbd1 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -51,7 +51,7 @@ public:
// 2) pull takes longer than mPullTimeoutNs (intrinsic to puller)
// If a metric wants to make any change to the data, like timestamps, it
// should make a copy as this data may be shared with multiple metrics.
- bool Pull(std::vector<std::shared_ptr<LogEvent>>* data);
+ bool Pull(const int64_t eventTimeNs, std::vector<std::shared_ptr<LogEvent>>* data);
// Clear cache immediately
int ForceClearCache();
@@ -94,6 +94,11 @@ private:
int64_t mLastPullTimeNs;
+ // All pulls happen due to an event (app upgrade, bucket boundary, condition change, etc).
+ // If multiple pulls need to be done at the same event time, we will always use the cache after
+ // the first pull.
+ int64_t mLastEventTimeNs;
+
// Cache of data from last pull. If next request comes before cool down finishes,
// cached data will be returned.
// Cached data is cleared when
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 1a52eb928777..8a9ec7456e55 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -91,20 +91,21 @@ StatsPullerManager::StatsPullerManager()
mPullAtomCallbackDeathRecipient(AIBinder_DeathRecipient_new(pullAtomCallbackDied)) {
}
-bool StatsPullerManager::Pull(int tagId, const ConfigKey& configKey,
+bool StatsPullerManager::Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
vector<shared_ptr<LogEvent>>* data, bool useUids) {
std::lock_guard<std::mutex> _l(mLock);
- return PullLocked(tagId, configKey, data, useUids);
+ return PullLocked(tagId, configKey, eventTimeNs, data, useUids);
}
-bool StatsPullerManager::Pull(int tagId, const vector<int32_t>& uids,
+bool StatsPullerManager::Pull(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool useUids) {
std::lock_guard<std::mutex> _l(mLock);
- return PullLocked(tagId, uids, data, useUids);
+ return PullLocked(tagId, uids, eventTimeNs, data, useUids);
}
bool StatsPullerManager::PullLocked(int tagId, const ConfigKey& configKey,
- vector<shared_ptr<LogEvent>>* data, bool useUids) {
+ const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data,
+ bool useUids) {
vector<int32_t> uids;
if (useUids) {
auto uidProviderIt = mPullUidProviders.find(configKey);
@@ -123,18 +124,19 @@ bool StatsPullerManager::PullLocked(int tagId, const ConfigKey& configKey,
}
uids = pullUidProvider->getPullAtomUids(tagId);
}
- return PullLocked(tagId, uids, data, useUids);
+ return PullLocked(tagId, uids, eventTimeNs, data, useUids);
}
bool StatsPullerManager::PullLocked(int tagId, const vector<int32_t>& uids,
- vector<shared_ptr<LogEvent>>* data, bool useUids) {
+ const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data,
+ bool useUids) {
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(data);
+ bool ret = pullerIt->second->Pull(eventTimeNs, data);
VLOG("pulled %zu items", data->size());
if (!ret) {
StatsdStats::getInstance().notePullFailed(tagId);
@@ -149,7 +151,7 @@ bool StatsPullerManager::PullLocked(int tagId, const vector<int32_t>& uids,
PullerKey key = {.atomTag = tagId, .uid = -1};
auto pullerIt = kAllPullAtomInfo.find(key);
if (pullerIt != kAllPullAtomInfo.end()) {
- bool ret = pullerIt->second->Pull(data);
+ bool ret = pullerIt->second->Pull(eventTimeNs, data);
VLOG("pulled %zu items", data->size());
if (!ret) {
StatsdStats::getInstance().notePullFailed(tagId);
@@ -290,7 +292,8 @@ void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) {
}
for (const auto& pullInfo : needToPull) {
vector<shared_ptr<LogEvent>> data;
- bool pullSuccess = PullLocked(pullInfo.first->atomTag, pullInfo.first->configKey, &data);
+ bool pullSuccess = PullLocked(pullInfo.first->atomTag, pullInfo.first->configKey,
+ elapsedTimeNs, &data);
if (!pullSuccess) {
VLOG("pull failed at %lld, will try again later", (long long)elapsedTimeNs);
}
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 5e18aaa6ed61..194a0f5edba8 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -101,11 +101,11 @@ public:
// registered for any of the uids for this atom.
// 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,
+ virtual bool Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool useUids = true);
// Same as above, but directly specify the allowed uids to pull from.
- virtual bool Pull(int tagId, const vector<int32_t>& uids,
+ virtual bool Pull(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data, bool useUids = true);
// Clear pull data cache immediately.
@@ -152,11 +152,11 @@ private:
// mapping from Config Key to the PullUidProvider for that config
std::map<ConfigKey, wp<PullUidProvider>> mPullUidProviders;
- bool PullLocked(int tagId, const ConfigKey& configKey, vector<std::shared_ptr<LogEvent>>* data,
- bool useUids = true);
+ bool PullLocked(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool useUids = true);
- bool PullLocked(int tagId, const vector<int32_t>& uids, vector<std::shared_ptr<LogEvent>>* data,
- bool useUids);
+ bool PullLocked(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool useUids);
// locks for data receiver and StatsCompanionService changes
std::mutex mLock;
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 21ffff32f539..d865c2176c1e 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -122,11 +122,11 @@ CountMetricProducer::~CountMetricProducer() {
}
void CountMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
- const HashableDimensionKey& primaryKey, int oldState,
- int newState) {
+ const HashableDimensionKey& primaryKey,
+ const FieldValue& oldState, const FieldValue& newState) {
VLOG("CountMetric %lld onStateChanged time %lld, State%d, key %s, %d -> %d",
(long long)mMetricId, (long long)eventTimeNs, atomId, primaryKey.toString().c_str(),
- oldState, newState);
+ oldState.mValue.int_value, newState.mValue.int_value);
}
void CountMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index f9a8842efc3d..26b3d3cc6722 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -53,8 +53,8 @@ public:
virtual ~CountMetricProducer();
void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
- const HashableDimensionKey& primaryKey, int oldState,
- int newState) override;
+ const HashableDimensionKey& primaryKey, const FieldValue& oldState,
+ const FieldValue& newState) override;
protected:
void onMatchedLogEventInternalLocked(
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 0de92f3d9f47..663365924829 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -161,13 +161,12 @@ sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker(
void DurationMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
const HashableDimensionKey& primaryKey,
- const int32_t oldState, const int32_t newState) {
- // Create a FieldValue object to hold the new state.
- FieldValue value;
- value.mValue.setInt(newState);
+ const FieldValue& oldState,
+ const FieldValue& newState) {
// Check if this metric has a StateMap. If so, map the new state value to
// the correct state group id.
- mapStateValue(atomId, &value);
+ FieldValue newStateCopy = newState;
+ mapStateValue(atomId, &newStateCopy);
flushIfNeededLocked(eventTimeNs);
@@ -185,7 +184,7 @@ void DurationMetricProducer::onStateChanged(const int64_t eventTimeNs, const int
if (!containsLinkedStateValues(whatIt.first, primaryKey, mMetric2StateLinks, atomId)) {
continue;
}
- whatIt.second->onStateChanged(eventTimeNs, atomId, value);
+ whatIt.second->onStateChanged(eventTimeNs, atomId, newStateCopy);
}
}
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 6f84076ee6b5..53f0f28c3386 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -55,8 +55,8 @@ public:
const sp<AlarmMonitor>& anomalyAlarmMonitor) override;
void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
- const HashableDimensionKey& primaryKey, const int32_t oldState,
- const int32_t newState) override;
+ const HashableDimensionKey& primaryKey, const FieldValue& oldState,
+ const FieldValue& newState) override;
protected:
void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) override;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index cc4c56537c4c..1d4d0b3a5e5d 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -321,7 +321,7 @@ void GaugeMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
return;
}
vector<std::shared_ptr<LogEvent>> allData;
- if (!mPullerManager->Pull(mPullTagId, mConfigKey, &allData)) {
+ if (!mPullerManager->Pull(mPullTagId, mConfigKey, timestampNs, &allData)) {
ALOGE("Gauge Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
return;
}
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 91c98ea27269..e86fdf06e836 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -182,8 +182,8 @@ public:
};
void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
- const HashableDimensionKey& primaryKey, const int32_t oldState,
- const int32_t newState){};
+ const HashableDimensionKey& primaryKey, const FieldValue& oldState,
+ const FieldValue& newState){};
// Output the metrics data to [protoOutput]. All metrics reports end with the same timestamp.
// This method clears all the past buckets.
@@ -442,7 +442,7 @@ protected:
bool mIsActive;
// The slice_by_state atom ids defined in statsd_config.
- std::vector<int32_t> mSlicedStateAtoms;
+ const std::vector<int32_t> mSlicedStateAtoms;
// Maps atom ids and state values to group_ids (<atom_id, <value, group_id>>).
const std::unordered_map<int32_t, std::unordered_map<int, int64_t>> mStateGroupMap;
@@ -459,6 +459,7 @@ protected:
FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap);
FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates);
FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields);
+ FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges);
FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
@@ -488,6 +489,7 @@ protected:
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState);
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions);
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions);
+ FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index d7ad27bd9134..e8c575a1adea 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -71,6 +71,8 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config,
mLastReportTimeNs(currentTimeNs),
mLastReportWallClockNs(getWallClockNs()),
mPullerManager(pullerManager),
+ mWhitelistedAtomIds(config.whitelisted_atom_ids().begin(),
+ config.whitelisted_atom_ids().end()),
mShouldPersistHistory(config.persist_locally()) {
// Init the ttl end timestamp.
refreshTtl(timeBaseNs);
@@ -366,11 +368,16 @@ void MetricsManager::onDumpReport(const int64_t dumpTimeStampNs,
bool MetricsManager::checkLogCredentials(const LogEvent& event) {
+ // TODO(b/154856835): Remove this check once we get whitelist from the config.
if (android::util::AtomsInfo::kWhitelistedAtoms.find(event.GetTagId()) !=
android::util::AtomsInfo::kWhitelistedAtoms.end())
{
return true;
}
+
+ if (mWhitelistedAtomIds.find(event.GetTagId()) != mWhitelistedAtomIds.end()) {
+ return true;
+ }
std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
if (mAllowedLogSources.find(event.GetUid()) == mAllowedLogSources.end()) {
VLOG("log source %d not on the whitelist", event.GetUid());
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index ef03d2064ab0..ad30a88c5d19 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -189,6 +189,8 @@ private:
// To guard access to mAllowedLogSources
mutable std::mutex mAllowedLogSourcesMutex;
+ const std::set<int32_t> mWhitelistedAtomIds;
+
// We can pull any atom from these uids.
std::set<int32_t> mDefaultPullUids;
@@ -290,9 +292,6 @@ private:
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation);
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition);
FRIEND_TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation);
FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket);
FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets);
@@ -320,6 +319,7 @@ private:
TestActivationOnBootMultipleActivationsDifferentActivationTypes);
FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
+ FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges);
FRIEND_TEST(CountMetricE2eTest, TestSlicedState);
FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap);
FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates);
@@ -337,6 +337,10 @@ private:
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSuperset);
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset);
+ FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation);
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState);
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions);
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index e5ec72e3d0f5..f03ce4550bc4 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -182,15 +182,26 @@ ValueMetricProducer::~ValueMetricProducer() {
}
void ValueMetricProducer::onStateChanged(int64_t eventTimeNs, int32_t atomId,
- const HashableDimensionKey& primaryKey, int oldState,
- int newState) {
+ const HashableDimensionKey& primaryKey,
+ const FieldValue& oldState, const FieldValue& newState) {
VLOG("ValueMetric %lld onStateChanged time %lld, State %d, key %s, %d -> %d",
(long long)mMetricId, (long long)eventTimeNs, atomId, primaryKey.toString().c_str(),
- oldState, newState);
+ oldState.mValue.int_value, newState.mValue.int_value);
// If condition is not true, we do not need to pull for this state change.
if (mCondition != ConditionState::kTrue) {
return;
}
+
+ // If old and new states are in the same StateGroup, then we do not need to
+ // pull for this state change.
+ FieldValue oldStateCopy = oldState;
+ FieldValue newStateCopy = newState;
+ mapStateValue(atomId, &oldStateCopy);
+ mapStateValue(atomId, &newStateCopy);
+ if (oldStateCopy == newStateCopy) {
+ return;
+ }
+
bool isEventLate = eventTimeNs < mCurrentBucketStartTimeNs;
if (isEventLate) {
VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
@@ -508,7 +519,7 @@ void ValueMetricProducer::prepareFirstBucketLocked() {
void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
vector<std::shared_ptr<LogEvent>> allData;
- if (!mPullerManager->Pull(mPullTagId, mConfigKey, &allData)) {
+ if (!mPullerManager->Pull(mPullTagId, mConfigKey, timestampNs, &allData)) {
ALOGE("Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
invalidateCurrentBucket(timestampNs, BucketDropReason::PULL_FAILED);
return;
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index c8dc8cc290c4..751fef2bf2b1 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -90,7 +90,7 @@ public:
};
void onStateChanged(int64_t eventTimeNs, int32_t atomId, const HashableDimensionKey& primaryKey,
- int oldState, int newState) override;
+ const FieldValue& oldState, const FieldValue& newState) override;
protected:
void onMatchedLogEventInternalLocked(
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 3ab44f4a06af..210d382b1363 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -791,10 +791,19 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
}
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()) {
- StateManager::getInstance().registerListener(atomId, it);
+ // 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;
diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp
index 7b687210ce33..361b161c76ac 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.cpp
+++ b/cmds/statsd/src/shell/ShellSubscriber.cpp
@@ -152,6 +152,7 @@ void ShellSubscriber::startPull(int myToken) {
}
int64_t nowMillis = getElapsedRealtimeMillis();
+ int64_t nowNanos = getElapsedRealtimeNs();
for (PullInfo& pullInfo : mSubscriptionInfo->mPulledInfo) {
if (pullInfo.mPrevPullElapsedRealtimeMs + pullInfo.mInterval >= nowMillis) {
continue;
@@ -161,7 +162,7 @@ void ShellSubscriber::startPull(int myToken) {
getUidsForPullAtom(&uids, pullInfo);
vector<std::shared_ptr<LogEvent>> data;
- mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), uids, &data);
+ mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), uids, nowNanos, &data);
VLOG("Pulled %zu atoms with id %d", data.size(), pullInfo.mPullerMatcher.atom_id());
writePulledAtomsLocked(data, pullInfo.mPullerMatcher);
diff --git a/cmds/statsd/src/state/StateListener.h b/cmds/statsd/src/state/StateListener.h
index d1af1968ac38..63880017ca18 100644
--- a/cmds/statsd/src/state/StateListener.h
+++ b/cmds/statsd/src/state/StateListener.h
@@ -45,8 +45,8 @@ public:
* [newState]: Current state value after state change
*/
virtual void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
- const HashableDimensionKey& primaryKey, int oldState,
- int newState) = 0;
+ const HashableDimensionKey& primaryKey, const FieldValue& oldState,
+ const FieldValue& newState) = 0;
};
} // namespace statsd
diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp
index b63713b64c5d..41e525c343ba 100644
--- a/cmds/statsd/src/state/StateTracker.cpp
+++ b/cmds/statsd/src/state/StateTracker.cpp
@@ -35,31 +35,30 @@ void StateTracker::onLogEvent(const LogEvent& event) {
HashableDimensionKey primaryKey;
filterPrimaryKey(event.getValues(), &primaryKey);
- FieldValue stateValue;
- if (!getStateFieldValueFromLogEvent(event, &stateValue)) {
+ FieldValue newState;
+ if (!getStateFieldValueFromLogEvent(event, &newState)) {
ALOGE("StateTracker error extracting state from log event. Missing exclusive state field.");
clearStateForPrimaryKey(eventTimeNs, primaryKey);
return;
}
- mField.setField(stateValue.mField.getField());
+ mField.setField(newState.mField.getField());
- if (stateValue.mValue.getType() != INT) {
+ if (newState.mValue.getType() != INT) {
ALOGE("StateTracker error extracting state from log event. Type: %d",
- stateValue.mValue.getType());
+ newState.mValue.getType());
clearStateForPrimaryKey(eventTimeNs, primaryKey);
return;
}
- const int32_t resetState = event.getResetState();
- if (resetState != -1) {
+ if (int resetState = event.getResetState(); resetState != -1) {
VLOG("StateTracker new reset state: %d", resetState);
- handleReset(eventTimeNs, resetState);
+ const FieldValue resetStateFieldValue(mField, Value(resetState));
+ handleReset(eventTimeNs, resetStateFieldValue);
return;
}
- const int32_t newState = stateValue.mValue.int_value;
- const bool nested = stateValue.mAnnotations.isNested();
+ const bool nested = newState.mAnnotations.isNested();
StateValueInfo* stateValueInfo = &mStateMap[primaryKey];
updateStateForPrimaryKey(eventTimeNs, primaryKey, newState, nested, stateValueInfo);
}
@@ -85,7 +84,7 @@ bool StateTracker::getStateValue(const HashableDimensionKey& queryKey, FieldValu
return false;
}
-void StateTracker::handleReset(const int64_t eventTimeNs, const int32_t newState) {
+void StateTracker::handleReset(const int64_t eventTimeNs, const FieldValue& newState) {
VLOG("StateTracker handle reset");
for (auto& [primaryKey, stateValueInfo] : mStateMap) {
updateStateForPrimaryKey(eventTimeNs, primaryKey, newState,
@@ -102,8 +101,9 @@ void StateTracker::clearStateForPrimaryKey(const int64_t eventTimeNs,
// If there is no entry for the primaryKey in mStateMap, then the state is already
// kStateUnknown.
+ const FieldValue state(mField, Value(kStateUnknown));
if (it != mStateMap.end()) {
- updateStateForPrimaryKey(eventTimeNs, primaryKey, kStateUnknown,
+ updateStateForPrimaryKey(eventTimeNs, primaryKey, state,
false /* nested; treat this state change as not nested */,
&it->second);
}
@@ -111,22 +111,26 @@ void StateTracker::clearStateForPrimaryKey(const int64_t eventTimeNs,
void StateTracker::updateStateForPrimaryKey(const int64_t eventTimeNs,
const HashableDimensionKey& primaryKey,
- const int32_t newState, const bool nested,
+ const FieldValue& newState, const bool nested,
StateValueInfo* stateValueInfo) {
- const int32_t oldState = stateValueInfo->state;
+ FieldValue oldState;
+ oldState.mField = mField;
+ oldState.mValue.setInt(stateValueInfo->state);
+ const int32_t oldStateValue = stateValueInfo->state;
+ const int32_t newStateValue = newState.mValue.int_value;
- if (kStateUnknown == newState) {
+ if (kStateUnknown == newStateValue) {
mStateMap.erase(primaryKey);
}
// Update state map for non-nested counting case.
// Every state event triggers a state overwrite.
if (!nested) {
- stateValueInfo->state = newState;
+ stateValueInfo->state = newStateValue;
stateValueInfo->count = 1;
// Notify listeners if state has changed.
- if (oldState != newState) {
+ if (oldStateValue != newStateValue) {
notifyListeners(eventTimeNs, primaryKey, oldState, newState);
}
return;
@@ -142,26 +146,26 @@ void StateTracker::updateStateForPrimaryKey(const int64_t eventTimeNs,
// In atoms.proto, a state atom with nested counting enabled
// must only have 2 states. There is no enforcemnt here of this requirement.
// The atom must be logged correctly.
- if (kStateUnknown == newState) {
- if (kStateUnknown != oldState) {
+ if (kStateUnknown == newStateValue) {
+ if (kStateUnknown != oldStateValue) {
notifyListeners(eventTimeNs, primaryKey, oldState, newState);
}
- } else if (oldState == kStateUnknown) {
- stateValueInfo->state = newState;
+ } else if (oldStateValue == kStateUnknown) {
+ stateValueInfo->state = newStateValue;
stateValueInfo->count = 1;
notifyListeners(eventTimeNs, primaryKey, oldState, newState);
- } else if (oldState == newState) {
+ } else if (oldStateValue == newStateValue) {
stateValueInfo->count++;
} else if (--stateValueInfo->count == 0) {
- stateValueInfo->state = newState;
+ stateValueInfo->state = newStateValue;
stateValueInfo->count = 1;
notifyListeners(eventTimeNs, primaryKey, oldState, newState);
}
}
void StateTracker::notifyListeners(const int64_t eventTimeNs,
- const HashableDimensionKey& primaryKey, const int32_t oldState,
- const int32_t newState) {
+ const HashableDimensionKey& primaryKey,
+ const FieldValue& oldState, const FieldValue& newState) {
for (auto l : mListeners) {
auto sl = l.promote();
if (sl != nullptr) {
diff --git a/cmds/statsd/src/state/StateTracker.h b/cmds/statsd/src/state/StateTracker.h
index c5f6315fc992..abd579e7e302 100644
--- a/cmds/statsd/src/state/StateTracker.h
+++ b/cmds/statsd/src/state/StateTracker.h
@@ -72,19 +72,19 @@ private:
std::set<wp<StateListener>> mListeners;
// Reset all state values in map to the given state.
- void handleReset(const int64_t eventTimeNs, const int32_t newState);
+ void handleReset(const int64_t eventTimeNs, const FieldValue& newState);
// Clears the state value mapped to the given primary key by setting it to kStateUnknown.
void clearStateForPrimaryKey(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey);
// Update the StateMap based on the received state value.
void updateStateForPrimaryKey(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey,
- const int32_t newState, const bool nested,
+ const FieldValue& newState, const bool nested,
StateValueInfo* stateValueInfo);
// Notify registered state listeners of state change.
void notifyListeners(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey,
- const int32_t oldState, const int32_t newState);
+ const FieldValue& oldState, const FieldValue& newState);
};
bool getStateFieldValueFromLogEvent(const LogEvent& event, FieldValue* output);
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index c7407bd9af1e..2e6043df0e64 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -131,7 +131,7 @@ message SimplePredicate {
UNKNOWN = 0;
FALSE = 1;
}
- optional InitialValue initial_value = 5 [default = FALSE];
+ optional InitialValue initial_value = 5 [default = UNKNOWN];
optional FieldMatcher dimensions = 6;
}
@@ -489,6 +489,8 @@ message StatsdConfig {
repeated PullAtomPackages pull_atom_packages = 23;
+ repeated int32 whitelisted_atom_ids = 24;
+
// Field number 1000 is reserved for later use.
reserved 1000;
}