Uid Sandboxing of Pullers
Overall flow of implementation:
1. parsing the config in MetricsManager to store the uids per atom. It
follows the mAllowedLogSources logic very closely
2. MetricsManager register itself as a PullUidProvider with the
PullerManager.
3. Metrics pass the config key when pulling (for both registering
receivers and normal pulls) , and the puller manager gets
the allowed uids from the PullUidProvider for that config.
4. PullerManager keys receivers by <atomId, configKey> so that it can
look up the uids for that atom using the PullUidProvider as well.
5. Added shell subscriber support. Hardcode a default of AID_SYSTEM for
them and also allow packages per atom. This involved adding a second
interface to Pull that simply accepts the uids, since I didnt want to
make the ShellSubscriber a PullUidProvider as well.
6. Change adb shell cmd stats pull-source to allow users to specify a
package. Default to AID_SYSTEM as well.
Notes:
The feature is flagged off right now, since configs do not pass in the
desired package. Another approach could be to hardcode in the current
mapping, but that doesn't work for OEM pulled atoms.
Test: m statsd
Test: bit statsd_test:* with useUids = false
Test: bit statsd_test:* with useUids = true
Bug: 144099783
Bug: 151978258
Change-Id: I4a7481d7402a52b9beb4ea28b102803f9e50e79f
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 69b9fc7..4966b2e 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -536,6 +536,7 @@
new MetricsManager(key, config, mTimeBaseNs, timestampNs, mUidMap, mPullerManager,
mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
if (newMetricsManager->isConfigValid()) {
+ newMetricsManager->init();
mUidMap->OnConfigUpdated(key);
newMetricsManager->refreshTtl(timestampNs);
mMetricsManagers[key] = newMetricsManager;
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 812d10b..98879a0 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -385,9 +385,11 @@
dprintf(out, " PKG Optional package name to print the uids of the package\n");
dprintf(out, "\n");
dprintf(out, "\n");
- dprintf(out, "usage: adb shell cmd stats pull-source [int] \n");
+ dprintf(out, "usage: adb shell cmd stats pull-source ATOM_TAG [PACKAGE] \n");
dprintf(out, "\n");
- dprintf(out, " Prints the output of a pulled metrics source (int indicates source)\n");
+ dprintf(out, " Prints the output of a pulled atom\n");
+ dprintf(out, " UID The atom to pull\n");
+ dprintf(out, " PACKAGE The package to pull from. Default is AID_SYSTEM\n");
dprintf(out, "\n");
dprintf(out, "\n");
dprintf(out, "usage: adb shell cmd stats write-to-disk \n");
@@ -806,8 +808,21 @@
status_t StatsService::cmd_print_pulled_metrics(int out, const Vector<String8>& args) {
int s = atoi(args[1].c_str());
- vector<shared_ptr<LogEvent> > stats;
- if (mPullerManager->Pull(s, &stats)) {
+ vector<int32_t> uids;
+ if (args.size() > 2) {
+ string package = string(args[2].c_str());
+ auto it = UidMap::sAidToUidMapping.find(package);
+ if (it != UidMap::sAidToUidMapping.end()) {
+ uids.push_back(it->second);
+ } else {
+ set<int32_t> uids_set = mUidMap->getAppUid(package);
+ uids.insert(uids.end(), uids_set.begin(), uids_set.end());
+ }
+ } else {
+ uids.push_back(AID_SYSTEM);
+ }
+ vector<shared_ptr<LogEvent>> stats;
+ if (mPullerManager->Pull(s, uids, &stats)) {
for (const auto& it : stats) {
dprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str());
}
diff --git a/cmds/statsd/src/external/PullUidProvider.h b/cmds/statsd/src/external/PullUidProvider.h
new file mode 100644
index 0000000..2318c50
--- /dev/null
+++ b/cmds/statsd/src/external/PullUidProvider.h
@@ -0,0 +1,39 @@
+/*
+ * 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 <utils/RefBase.h>
+
+#include "StatsPuller.h"
+#include "logd/LogEvent.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class PullUidProvider : virtual public RefBase {
+public:
+ virtual ~PullUidProvider() {}
+
+ /**
+ * @param atomId The atom for which to get the uids.
+ */
+ virtual vector<int32_t> getPullAtomUids(int32_t atomId) = 0;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 8b6a5a1..4ffa3d8 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -47,27 +47,73 @@
StatsPullerManager::StatsPullerManager()
: kAllPullAtomInfo({
// TrainInfo.
- {{.atomTag = util::TRAIN_INFO}, new TrainInfoPuller()},
+ {{.atomTag = util::TRAIN_INFO, .uid = -1}, new TrainInfoPuller()},
}),
mNextPullTimeNs(NO_ALARM_UPDATE) {
}
-bool StatsPullerManager::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) {
- AutoMutex _l(mLock);
- return PullLocked(tagId, data);
+bool StatsPullerManager::Pull(int tagId, const ConfigKey& configKey,
+ vector<shared_ptr<LogEvent>>* data, bool useUids) {
+ std::lock_guard<std::mutex> _l(mLock);
+ return PullLocked(tagId, configKey, data, useUids);
}
-bool StatsPullerManager::PullLocked(int tagId, vector<shared_ptr<LogEvent>>* data) {
- VLOG("Initiating pulling %d", tagId);
+bool StatsPullerManager::Pull(int tagId, const vector<int32_t>& uids,
+ vector<std::shared_ptr<LogEvent>>* data, bool useUids) {
+ std::lock_guard<std::mutex> _l(mLock);
+ return PullLocked(tagId, uids, data, useUids);
+}
- if (kAllPullAtomInfo.find({.atomTag = tagId}) != kAllPullAtomInfo.end()) {
- bool ret = kAllPullAtomInfo.find({.atomTag = tagId})->second->Pull(data);
- VLOG("pulled %d items", (int)data->size());
- if (!ret) {
- StatsdStats::getInstance().notePullFailed(tagId);
+bool StatsPullerManager::PullLocked(int tagId, const ConfigKey& configKey,
+ vector<shared_ptr<LogEvent>>* data, bool useUids) {
+ 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());
+ return false;
}
- return ret;
+ 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());
+ return false;
+ }
+ uids = pullUidProvider->getPullAtomUids(tagId);
+ }
+ return PullLocked(tagId, uids, data, useUids);
+}
+
+bool StatsPullerManager::PullLocked(int tagId, const vector<int32_t>& uids,
+ 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);
+ VLOG("pulled %zu items", data->size());
+ if (!ret) {
+ StatsdStats::getInstance().notePullFailed(tagId);
+ }
+ return ret;
+ }
+ }
+ 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};
+ auto pullerIt = kAllPullAtomInfo.find(key);
+ if (pullerIt != kAllPullAtomInfo.end()) {
+ bool ret = pullerIt->second->Pull(data);
+ VLOG("pulled %zu items", data->size());
+ if (!ret) {
+ StatsdStats::getInstance().notePullFailed(tagId);
+ }
+ return ret;
+ }
ALOGW("StatsPullerManager: Unknown tagId %d", tagId);
return false; // Return early since we don't know what to pull.
}
@@ -96,7 +142,7 @@
void StatsPullerManager::SetStatsCompanionService(
shared_ptr<IStatsCompanionService> statsCompanionService) {
- AutoMutex _l(mLock);
+ std::lock_guard<std::mutex> _l(mLock);
shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService;
mStatsCompanionService = statsCompanionService;
for (const auto& pulledAtom : kAllPullAtomInfo) {
@@ -107,10 +153,11 @@
}
}
-void StatsPullerManager::RegisterReceiver(int tagId, wp<PullDataReceiver> receiver,
- int64_t nextPullTimeNs, int64_t intervalNs) {
- AutoMutex _l(mLock);
- auto& receivers = mReceivers[tagId];
+void StatsPullerManager::RegisterReceiver(int tagId, const ConfigKey& configKey,
+ wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
+ int64_t intervalNs) {
+ std::lock_guard<std::mutex> _l(mLock);
+ auto& receivers = mReceivers[{.atomTag = tagId, .configKey = configKey}];
for (auto it = receivers.begin(); it != receivers.end(); it++) {
if (it->receiver == receiver) {
VLOG("Receiver already registered of %d", (int)receivers.size());
@@ -142,13 +189,15 @@
VLOG("Puller for tagId %d registered of %d", tagId, (int)receivers.size());
}
-void StatsPullerManager::UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver) {
- AutoMutex _l(mLock);
- if (mReceivers.find(tagId) == mReceivers.end()) {
+void StatsPullerManager::UnRegisterReceiver(int tagId, const ConfigKey& configKey,
+ wp<PullDataReceiver> receiver) {
+ std::lock_guard<std::mutex> _l(mLock);
+ auto receiversIt = mReceivers.find({.atomTag = tagId, .configKey = configKey});
+ if (receiversIt == mReceivers.end()) {
VLOG("Unknown pull code or no receivers: %d", tagId);
return;
}
- auto& receivers = mReceivers.find(tagId)->second;
+ std::list<ReceiverInfo>& receivers = receiversIt->second;
for (auto it = receivers.begin(); it != receivers.end(); it++) {
if (receiver == it->receiver) {
receivers.erase(it);
@@ -158,16 +207,26 @@
}
}
+void StatsPullerManager::RegisterPullUidProvider(const ConfigKey& configKey,
+ wp<PullUidProvider> provider) {
+ std::lock_guard<std::mutex> _l(mLock);
+ mPullUidProviders[configKey] = provider;
+}
+
+void StatsPullerManager::UnregisterPullUidProvider(const ConfigKey& configKey) {
+ std::lock_guard<std::mutex> _l(mLock);
+ mPullUidProviders.erase(configKey);
+}
+
void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) {
- AutoMutex _l(mLock);
+ std::lock_guard<std::mutex> _l(mLock);
int64_t wallClockNs = getWallClockNs();
int64_t minNextPullTimeNs = NO_ALARM_UPDATE;
- vector<pair<int, vector<ReceiverInfo*>>> needToPull =
- vector<pair<int, vector<ReceiverInfo*>>>();
+ vector<pair<const ReceiverKey*, vector<ReceiverInfo*>>> needToPull;
for (auto& pair : mReceivers) {
- vector<ReceiverInfo*> receivers = vector<ReceiverInfo*>();
+ vector<ReceiverInfo*> receivers;
if (pair.second.size() != 0) {
for (ReceiverInfo& receiverInfo : pair.second) {
if (receiverInfo.nextPullTimeNs <= elapsedTimeNs) {
@@ -179,17 +238,16 @@
}
}
if (receivers.size() > 0) {
- needToPull.push_back(make_pair(pair.first, receivers));
+ needToPull.push_back(make_pair(&pair.first, receivers));
}
}
}
-
for (const auto& pullInfo : needToPull) {
vector<shared_ptr<LogEvent>> data;
- bool pullSuccess = PullLocked(pullInfo.first, &data);
+ bool pullSuccess = PullLocked(pullInfo.first->atomTag, pullInfo.first->configKey, &data);
if (pullSuccess) {
- StatsdStats::getInstance().notePullDelay(
- pullInfo.first, getElapsedRealtimeNs() - elapsedTimeNs);
+ StatsdStats::getInstance().notePullDelay(pullInfo.first->atomTag,
+ getElapsedRealtimeNs() - elapsedTimeNs);
} else {
VLOG("pull failed at %lld, will try again later", (long long)elapsedTimeNs);
}
@@ -248,18 +306,19 @@
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) {
- AutoMutex _l(mLock);
+ const shared_ptr<IPullAtomCallback>& callback,
+ bool useUid) {
+ std::lock_guard<std::mutex> _l(mLock);
VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag);
// TODO(b/146439412): linkToDeath with the callback so that we can remove it
// and delete the puller.
StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/true);
- kAllPullAtomInfo[{.atomTag = atomTag}] =
+ kAllPullAtomInfo[{.atomTag = atomTag, .uid = useUid ? uid : -1}] =
new StatsCallbackPuller(atomTag, callback, coolDownNs, timeoutNs, additiveFields);
}
void StatsPullerManager::UnregisterPullAtomCallback(const int uid, const int32_t atomTag) {
- AutoMutex _l(mLock);
+ std::lock_guard<std::mutex> _l(mLock);
StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/false);
kAllPullAtomInfo.erase({.atomTag = atomTag});
}
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index e067766..714b0ae 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -19,15 +19,16 @@
#include <aidl/android/os/IPullAtomCallback.h>
#include <aidl/android/os/IStatsCompanionService.h>
#include <utils/RefBase.h>
-#include <utils/threads.h>
#include <list>
#include <vector>
#include "PullDataReceiver.h"
+#include "PullUidProvider.h"
#include "StatsPuller.h"
#include "guardrail/StatsdStats.h"
#include "logd/LogEvent.h"
+#include "packages/UidMap.h"
using aidl::android::os::IPullAtomCallback;
using aidl::android::os::IStatsCompanionService;
@@ -67,11 +68,20 @@
// Registers a receiver for tagId. It will be pulled on the nextPullTimeNs
// and then every intervalNs thereafter.
- virtual void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
+ virtual void RegisterReceiver(int tagId, const ConfigKey& configKey,
+ wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
int64_t intervalNs);
// Stop listening on a tagId.
- virtual void UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver);
+ virtual void UnRegisterReceiver(int tagId, const ConfigKey& configKey,
+ wp<PullDataReceiver> receiver);
+
+ // Registers a pull uid provider for the config key. When pulling atoms, it will be used to
+ // determine which atoms to pull from.
+ virtual void RegisterPullUidProvider(const ConfigKey& configKey, wp<PullUidProvider> provider);
+
+ // Unregister a pull uid provider.
+ virtual void UnregisterPullUidProvider(const ConfigKey& configKey);
// Verify if we know how to pull for this matcher
bool PullerForMatcherExists(int tagId) const;
@@ -85,9 +95,16 @@
// Returns false when
// 1) the pull fails
// 2) pull takes longer than mPullTimeoutNs (intrinsic to puller)
+ // 3) Either a PullUidProvider was not registered for the config, or the there was no puller
+ // 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, vector<std::shared_ptr<LogEvent>>* data);
+ virtual bool Pull(int tagId, const ConfigKey& configKey,
+ vector<std::shared_ptr<LogEvent>>* data, bool useUids = false);
+
+ // Same as above, but directly specify the allowed uids to pull from.
+ virtual bool Pull(int tagId, const vector<int32_t>& uids,
+ vector<std::shared_ptr<LogEvent>>* data, bool useUids = false);
// Clear pull data cache immediately.
int ForceClearPullerCache();
@@ -99,7 +116,8 @@
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);
+ const shared_ptr<IPullAtomCallback>& callback,
+ bool useUid = false);
void UnregisterPullAtomCallback(const int uid, const int32_t atomTag);
@@ -108,19 +126,36 @@
private:
shared_ptr<IStatsCompanionService> mStatsCompanionService = nullptr;
+ // A struct containing an atom id and a Config Key
+ typedef struct ReceiverKey {
+ const int atomTag;
+ const ConfigKey configKey;
+
+ inline bool operator<(const ReceiverKey& that) const {
+ return atomTag == that.atomTag ? configKey < that.configKey : atomTag < that.atomTag;
+ }
+ } ReceiverKey;
+
typedef struct {
int64_t nextPullTimeNs;
int64_t intervalNs;
wp<PullDataReceiver> receiver;
} ReceiverInfo;
- // mapping from simple matcher tagId to receivers
- std::map<int, std::list<ReceiverInfo>> mReceivers;
+ // mapping from Receiver Key to receivers
+ std::map<ReceiverKey, std::list<ReceiverInfo>> mReceivers;
- bool PullLocked(int tagId, vector<std::shared_ptr<LogEvent>>* data);
+ // 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 = false);
+
+ bool PullLocked(int tagId, const vector<int32_t>& uids, vector<std::shared_ptr<LogEvent>>* data,
+ bool useUids);
// locks for data receiver and StatsCompanionService changes
- Mutex mLock;
+ std::mutex mLock;
void updateAlarmLocked();
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index df810aa..25794c8 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -119,6 +119,8 @@
const static int kMaxLogSourceCount = 50;
+ const static int kMaxPullAtomPackages = 100;
+
// Max memory allowed for storing metrics per configuration. If this limit is exceeded, statsd
// drops the metrics data in memory.
static const size_t kMaxMetricsBytesPerConfig = 2 * 1024 * 1024;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 5c606bc..42bbd8e 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -75,11 +75,9 @@
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,
- const vector<int>& slicedStateAtoms,
- const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
+ const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap)
: MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap,
- eventDeactivationMap, slicedStateAtoms, stateGroupMap),
+ eventDeactivationMap, /*slicedStateAtoms=*/{}, /*stateGroupMap=*/{}),
mWhatMatcherIndex(whatMatcherIndex),
mEventMatcherWizard(matcherWizard),
mPullerManager(pullerManager),
@@ -135,15 +133,12 @@
flushIfNeededLocked(startTimeNs);
// Kicks off the puller immediately.
if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
- mPullerManager->RegisterReceiver(mPullTagId, this, getCurrentBucketEndTimeNs(),
+ mPullerManager->RegisterReceiver(mPullTagId, mConfigKey, this, getCurrentBucketEndTimeNs(),
mBucketSizeNs);
}
// Adjust start for partial first bucket and then pull if needed
mCurrentBucketStartTimeNs = startTimeNs;
- if (mIsActive && mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
- pullAndMatchEventsLocked(mCurrentBucketStartTimeNs);
- }
VLOG("Gauge metric %lld created. bucket size %lld start_time: %lld sliced %d",
(long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs,
@@ -153,7 +148,7 @@
GaugeMetricProducer::~GaugeMetricProducer() {
VLOG("~GaugeMetricProducer() called");
if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
- mPullerManager->UnRegisterReceiver(mPullTagId, this);
+ mPullerManager->UnRegisterReceiver(mPullTagId, mConfigKey, this);
}
}
@@ -298,6 +293,11 @@
}
}
+void GaugeMetricProducer::prepareFirstBucketLocked() {
+ if (mIsActive && mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
+ pullAndMatchEventsLocked(mCurrentBucketStartTimeNs);
+ }
+}
void GaugeMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
bool triggerPuller = false;
@@ -323,7 +323,7 @@
return;
}
vector<std::shared_ptr<LogEvent>> allData;
- if (!mPullerManager->Pull(mPullTagId, &allData)) {
+ if (!mPullerManager->Pull(mPullTagId, mConfigKey, &allData)) {
ALOGE("Gauge Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
return;
}
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 284bcc5..79ec711 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -64,9 +64,7 @@
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 = {},
- const vector<int>& slicedStateAtoms = {},
- const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
+ eventDeactivationMap = {});
virtual ~GaugeMetricProducer();
@@ -129,6 +127,8 @@
void flushCurrentBucketLocked(const int64_t& eventTimeNs,
const int64_t& nextBucketStartTimeNs) override;
+ void prepareFirstBucketLocked() override;
+
void pullAndMatchEventsLocked(const int64_t timestampNs);
const int mWhatMatcherIndex;
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index d721514..4c4cd89 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -207,6 +207,11 @@
return clearPastBucketsLocked(dumpTimeNs);
}
+ void prepareFirstBucket() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ prepareFirstBucketLocked();
+ }
+
// Returns the memory in bytes currently used to store this metric's data. Does not change
// state.
size_t byteSize() const {
@@ -344,6 +349,7 @@
std::set<string> *str_set,
android::util::ProtoOutputStream* protoOutput) = 0;
virtual void clearPastBucketsLocked(const int64_t dumpTimeNs) = 0;
+ virtual void prepareFirstBucketLocked(){};
virtual size_t byteSizeLocked() const = 0;
virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0;
virtual void dropDataLocked(const int64_t dropTimeNs) = 0;
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index fca48f9..8ed0cbc 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -70,6 +70,7 @@
mTtlEndNs(-1),
mLastReportTimeNs(currentTimeNs),
mLastReportWallClockNs(getWallClockNs()),
+ mPullerManager(pullerManager),
mShouldPersistHistory(config.persist_locally()) {
// Init the ttl end timestamp.
refreshTtl(timeBaseNs);
@@ -86,6 +87,7 @@
mVersionStringsInReport = config.version_strings_in_metric_report();
mInstallerInReport = config.installer_in_metric_report();
+ // Init allowed pushed atom uids.
if (config.allowed_log_source_size() == 0) {
mConfigValid = false;
ALOGE("Log source whitelist is empty! This config won't get any data. Suggest adding at "
@@ -108,6 +110,40 @@
}
}
+ // Init default allowed pull atom uids.
+ int numPullPackages = 0;
+ for (const string& pullSource : config.default_pull_packages()) {
+ auto it = UidMap::sAidToUidMapping.find(pullSource);
+ if (it != UidMap::sAidToUidMapping.end()) {
+ numPullPackages++;
+ mDefaultPullUids.insert(it->second);
+ } else {
+ ALOGE("Default pull atom packages must be in sAidToUidMapping");
+ mConfigValid = false;
+ }
+ }
+ // Init per-atom pull atom packages.
+ for (const PullAtomPackages& pullAtomPackages : config.pull_atom_packages()) {
+ int32_t atomId = pullAtomPackages.atom_id();
+ for (const string& pullPackage : pullAtomPackages.packages()) {
+ numPullPackages++;
+ auto it = UidMap::sAidToUidMapping.find(pullPackage);
+ if (it != UidMap::sAidToUidMapping.end()) {
+ mPullAtomUids[atomId].insert(it->second);
+ } else {
+ mPullAtomPackages[atomId].insert(pullPackage);
+ }
+ }
+ }
+ if (numPullPackages > StatsdStats::kMaxPullAtomPackages) {
+ ALOGE("Too many sources in default_pull_packages and pull_atom_packages. This is likely to "
+ "be an error in the config");
+ mConfigValid = false;
+ } 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());
@@ -153,6 +189,7 @@
StateManager::getInstance().unregisterListener(atomId, it);
}
}
+ mPullerManager->UnregisterPullUidProvider(mConfigKey);
VLOG("~MetricsManager()");
}
@@ -173,6 +210,20 @@
}
}
+void MetricsManager::initPullAtomSources() {
+ std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
+ mCombinedPullAtomUids.clear();
+ for (const auto& [atomId, uids] : mPullAtomUids) {
+ mCombinedPullAtomUids[atomId].insert(uids.begin(), uids.end());
+ }
+ for (const auto& [atomId, packages] : mPullAtomPackages) {
+ for (const string& pkg : packages) {
+ set<int32_t> uids = mUidMap->getAppUid(pkg);
+ mCombinedPullAtomUids[atomId].insert(uids.begin(), uids.end());
+ }
+ }
+}
+
bool MetricsManager::isConfigValid() const {
return mConfigValid;
}
@@ -184,12 +235,18 @@
it->notifyAppUpgrade(eventTimeNs, apk, uid, version);
}
// check if we care this package
- if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) == mAllowedPkg.end()) {
- return;
+ 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();
}
- // 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();
+
+ for (const auto& it : mPullAtomPackages) {
+ if (it.second.find(apk) != it.second.end()) {
+ initPullAtomSources();
+ return;
+ }
+ }
}
void MetricsManager::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk,
@@ -199,24 +256,49 @@
it->notifyAppRemoved(eventTimeNs, apk, uid);
}
// check if we care this package
- if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) == mAllowedPkg.end()) {
- return;
+ 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();
}
- // 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();
+
+ for (const auto& it : mPullAtomPackages) {
+ if (it.second.find(apk) != it.second.end()) {
+ initPullAtomSources();
+ return;
+ }
+ }
}
void MetricsManager::onUidMapReceived(const int64_t& eventTimeNs) {
// Purposefully don't inform metric producers on a new snapshot
// because we don't need to flush partial buckets.
// This occurs if a new user is added/removed or statsd crashes.
+ initPullAtomSources();
+
if (mAllowedPkg.size() == 0) {
return;
}
initLogSourceWhiteList();
}
+void MetricsManager::init() {
+ for (const auto& producer : mAllMetricProducers) {
+ producer->prepareFirstBucket();
+ }
+}
+
+vector<int32_t> MetricsManager::getPullAtomUids(int32_t atomId) {
+ std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
+ vector<int32_t> uids;
+ const auto& it = mCombinedPullAtomUids.find(atomId);
+ if (it != mCombinedPullAtomUids.end()) {
+ uids.insert(uids.end(), it->second.begin(), it->second.end());
+ }
+ uids.insert(uids.end(), mDefaultPullUids.begin(), mDefaultPullUids.end());
+ return uids;
+}
+
void MetricsManager::dumpStates(FILE* out, bool verbose) {
fprintf(out, "ConfigKey %s, allowed source:", mConfigKey.ToString().c_str());
{
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 7500ec9..291f97b 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -35,7 +35,7 @@
namespace statsd {
// A MetricsManager is responsible for managing metrics from one single config source.
-class MetricsManager : public virtual android::RefBase {
+class MetricsManager : public virtual android::RefBase, public virtual PullUidProvider {
public:
MetricsManager(const ConfigKey& configKey, const StatsdConfig& config, const int64_t timeBaseNs,
const int64_t currentTimeNs, const sp<UidMap>& uidMap,
@@ -69,6 +69,10 @@
void onUidMapReceived(const int64_t& eventTimeNs);
+ void init();
+
+ vector<int32_t> getPullAtomUids(int32_t atomId) override;
+
bool shouldWriteToDisk() const {
return mNoReportMetricIds.size() != mAllMetricProducers.size();
}
@@ -159,6 +163,8 @@
int64_t mLastReportTimeNs;
int64_t mLastReportWallClockNs;
+ sp<StatsPullerManager> mPullerManager;
+
// The uid log sources from StatsdConfig.
std::vector<int32_t> mAllowedUid;
@@ -169,13 +175,27 @@
// Logs from uids that are not in the list will be ignored to avoid spamming.
std::set<int32_t> mAllowedLogSources;
+ // To guard access to mAllowedLogSources
+ mutable std::mutex mAllowedLogSourcesMutex;
+
+ // We can pull any atom from these uids.
+ std::set<int32_t> mDefaultPullUids;
+
+ // Uids that specific atoms can pull from.
+ // This is a map<atom id, set<uids>>
+ std::map<int32_t, std::set<int32_t>> mPullAtomUids;
+
+ // Packages that specific atoms can be pulled from.
+ std::map<int32_t, std::set<std::string>> mPullAtomPackages;
+
+ // All uids to pull for this atom. NOTE: Does not include the default uids for memory.
+ std::map<int32_t, std::set<int32_t>> mCombinedPullAtomUids;
+
// Contains the annotations passed in with StatsdConfig.
std::list<std::pair<const int64_t, const int32_t>> mAnnotations;
const bool mShouldPersistHistory;
- // To guard access to mAllowedLogSources
- mutable std::mutex mAllowedLogSourcesMutex;
// All event tags that are interesting to my metrics.
std::set<int> mTagIds;
@@ -238,6 +258,8 @@
void initLogSourceWhiteList();
+ void initPullAtomSources();
+
// The metrics that don't need to be uploaded or even reported.
std::set<int64_t> mNoReportMetricIds;
@@ -275,6 +297,8 @@
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
+ FRIEND_TEST(MetricsManagerTest, TestLogSources);
+
FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index dc9b413..f34423a 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -156,7 +156,7 @@
flushIfNeededLocked(startTimeNs);
if (mIsPulled) {
- mPullerManager->RegisterReceiver(mPullTagId, this, getCurrentBucketEndTimeNs(),
+ mPullerManager->RegisterReceiver(mPullTagId, mConfigKey, this, getCurrentBucketEndTimeNs(),
mBucketSizeNs);
}
@@ -166,10 +166,6 @@
mCurrentBucketStartTimeNs = startTimeNs;
mConditionTimer.newBucketStart(mCurrentBucketStartTimeNs);
- // Kicks off the puller immediately if condition is true and diff based.
- if (mIsActive && mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) {
- pullAndMatchEventsLocked(mCurrentBucketStartTimeNs);
- }
// Now that activations are processed, start the condition timer if needed.
mConditionTimer.onConditionChanged(mIsActive && mCondition == ConditionState::kTrue,
mCurrentBucketStartTimeNs);
@@ -181,7 +177,7 @@
ValueMetricProducer::~ValueMetricProducer() {
VLOG("~ValueMetricProducer() called");
if (mIsPulled) {
- mPullerManager->UnRegisterReceiver(mPullTagId, this);
+ mPullerManager->UnRegisterReceiver(mPullTagId, mConfigKey, this);
}
}
@@ -503,9 +499,16 @@
mConditionTimer.onConditionChanged(mCondition, eventTimeNs);
}
+void ValueMetricProducer::prepareFirstBucketLocked() {
+ // Kicks off the puller immediately if condition is true and diff based.
+ if (mIsActive && mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) {
+ pullAndMatchEventsLocked(mCurrentBucketStartTimeNs);
+ }
+}
+
void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
vector<std::shared_ptr<LogEvent>> allData;
- if (!mPullerManager->Pull(mPullTagId, &allData)) {
+ if (!mPullerManager->Pull(mPullTagId, mConfigKey, &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 50317b3..e9273dc 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -122,6 +122,8 @@
void flushCurrentBucketLocked(const int64_t& eventTimeNs,
const int64_t& nextBucketStartTimeNs) override;
+ void prepareFirstBucketLocked() override;
+
void dropDataLocked(const int64_t dropTimeNs) override;
// Calculate previous bucket end time based on current time.
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index e5fe87a..3810c48 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -803,9 +803,8 @@
if (!success) return false;
sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(
- key, metric, conditionIndex, wizard,
- trackerIndex, matcherWizard, pullTagId, triggerAtomId, atomTagId,
- timeBaseTimeNs, currentTimeNs, pullerManager,
+ key, metric, conditionIndex, wizard, trackerIndex, matcherWizard, pullTagId,
+ triggerAtomId, atomTagId, timeBaseTimeNs, currentTimeNs, pullerManager,
eventActivationMap, eventDeactivationMap);
allMetricProducers.push_back(gaugeProducer);
}
@@ -964,6 +963,7 @@
ALOGE("initConditionTrackers failed");
return false;
}
+
if (!initStates(config, stateAtomIdMap, allStateGroupMaps)) {
ALOGE("initStates failed");
return false;
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index ab0e86e..acf40c8 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -545,7 +545,15 @@
{"AID_LMKD", 1069},
{"AID_LLKD", 1070},
{"AID_IORAPD", 1071},
+ {"AID_GPU_SERVICE", 1072},
{"AID_NETWORK_STACK", 1073},
+ {"AID_GSID", 1074},
+ {"AID_FSVERITY_CERT", 1075},
+ {"AID_CREDSTORE", 1076},
+ {"AID_EXTERNAL_STORAGE", 1077},
+ {"AID_EXT_DATA_RW", 1078},
+ {"AID_EXT_OBB_RW", 1079},
+ {"AID_CONTEXT_HUB", 1080},
{"AID_SHELL", 2000},
{"AID_CACHE", 2001},
{"AID_DIAG", 2002}};
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index 02fe7b1..22250ae 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -149,7 +149,7 @@
// Get currently cached value of memory used by UID map.
size_t getBytesUsed() const;
- std::set<int32_t> getAppUid(const string& package) const;
+ virtual std::set<int32_t> getAppUid(const string& package) const;
// Write current PackageInfoSnapshot to ProtoOutputStream.
// interestingUids: If not empty, only write the package info for these uids. If empty, write
diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp
index c677222..1cee4d9 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.cpp
+++ b/cmds/statsd/src/shell/ShellSubscriber.cpp
@@ -108,7 +108,21 @@
if (minInterval < 0 || pulled.freq_millis() < minInterval) {
minInterval = pulled.freq_millis();
}
- subscriptionInfo->mPulledInfo.emplace_back(pulled.matcher(), pulled.freq_millis());
+
+ vector<string> packages;
+ vector<int32_t> uids;
+ for (const string& pkg : pulled.packages()) {
+ auto it = UidMap::sAidToUidMapping.find(pkg);
+ if (it != UidMap::sAidToUidMapping.end()) {
+ uids.push_back(it->second);
+ } else {
+ packages.push_back(pkg);
+ }
+ }
+
+ subscriptionInfo->mPulledInfo.emplace_back(pulled.matcher(), pulled.freq_millis(), packages,
+ uids);
+ VLOG("adding matcher for pulled atom %d", pulled.matcher().atom_id());
}
subscriptionInfo->mPullIntervalMin = minInterval;
@@ -127,7 +141,15 @@
for (auto& pullInfo : mSubscriptionInfo->mPulledInfo) {
if (pullInfo.mPrevPullElapsedRealtimeMs + pullInfo.mInterval < nowMillis) {
vector<std::shared_ptr<LogEvent>> data;
- mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), &data);
+ vector<int32_t> uids;
+ uids.insert(uids.end(), pullInfo.mPullUids.begin(), pullInfo.mPullUids.end());
+ // This is slow. Consider storing the uids per app and listening to uidmap updates.
+ for (const string& pkg : pullInfo.mPullPackages) {
+ set<int32_t> uidsForPkg = mUidMap->getAppUid(pkg);
+ uids.insert(uids.end(), uidsForPkg.begin(), uidsForPkg.end());
+ }
+ uids.push_back(DEFAULT_PULL_UID);
+ mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), uids, &data);
VLOG("pulled %zu atoms with id %d", data.size(), pullInfo.mPullerMatcher.atom_id());
// TODO(b/150969574): Don't write to a pipe while holding a lock.
diff --git a/cmds/statsd/src/shell/ShellSubscriber.h b/cmds/statsd/src/shell/ShellSubscriber.h
index 2f9b61a..61457d8 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.h
+++ b/cmds/statsd/src/shell/ShellSubscriber.h
@@ -16,15 +16,17 @@
#pragma once
-#include "logd/LogEvent.h"
-
#include <android/util/ProtoOutputStream.h>
+#include <private/android_filesystem_config.h>
+
#include <condition_variable>
#include <mutex>
#include <thread>
+
#include "external/StatsPullerManager.h"
#include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "logd/LogEvent.h"
#include "packages/UidMap.h"
namespace android {
@@ -66,12 +68,19 @@
private:
struct PullInfo {
- PullInfo(const SimpleAtomMatcher& matcher, int64_t interval)
- : mPullerMatcher(matcher), mInterval(interval), mPrevPullElapsedRealtimeMs(0) {
+ PullInfo(const SimpleAtomMatcher& matcher, int64_t interval,
+ const std::vector<std::string>& packages, const std::vector<int32_t>& uids)
+ : mPullerMatcher(matcher),
+ mInterval(interval),
+ mPrevPullElapsedRealtimeMs(0),
+ mPullPackages(packages),
+ mPullUids(uids) {
}
SimpleAtomMatcher mPullerMatcher;
int64_t mInterval;
int64_t mPrevPullElapsedRealtimeMs;
+ std::vector<std::string> mPullPackages;
+ std::vector<int32_t> mPullUids;
};
struct SubscriptionInfo {
@@ -109,6 +118,8 @@
std::shared_ptr<SubscriptionInfo> mSubscriptionInfo = nullptr;
int mToken = 0;
+
+ const int32_t DEFAULT_PULL_UID = AID_SYSTEM;
};
} // namespace statsd
diff --git a/cmds/statsd/src/shell/shell_config.proto b/cmds/statsd/src/shell/shell_config.proto
index 73cb49a..07d0310 100644
--- a/cmds/statsd/src/shell/shell_config.proto
+++ b/cmds/statsd/src/shell/shell_config.proto
@@ -28,6 +28,9 @@
/* gap between two pulls in milliseconds */
optional int32 freq_millis = 2;
+
+ /* Packages that the pull is requested from */
+ repeated string packages = 3;
}
message ShellSubscription {
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 736aa9b..83d9484 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -428,6 +428,12 @@
repeated EventActivation event_activation = 2;
}
+message PullAtomPackages {
+ optional int32 atom_id = 1;
+
+ repeated string packages = 2;
+}
+
message StatsdConfig {
optional int64 id = 1;
@@ -475,6 +481,10 @@
repeated State state = 21;
+ repeated string default_pull_packages = 22;
+
+ repeated PullAtomPackages pull_atom_packages = 23;
+
// Field number 1000 is reserved for later use.
reserved 1000;
}