1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #define STATSD_DEBUG false // STOPSHIP if true
17 #if !defined(NDEBUG) && !defined(DEBUG)
18 #define NDEBUG // comment to enable assert
19 #endif /* !defined(NDEBUG) && !defined(DEBUG) */
20 #include "Log.h"
21
22 #include "MetricsManager.h"
23
24 #include <assert.h>
25 #include <private/android_filesystem_config.h>
26
27 #include "CountMetricProducer.h"
28 #include "condition/CombinationConditionTracker.h"
29 #include "condition/SimpleConditionTracker.h"
30 #include "flags/FlagProvider.h"
31 #include "guardrail/StatsdStats.h"
32 #include "matchers/CombinationAtomMatchingTracker.h"
33 #include "matchers/SimpleAtomMatchingTracker.h"
34 #include "parsing_utils/config_update_utils.h"
35 #include "parsing_utils/metrics_manager_util.h"
36 #include "state/StateManager.h"
37 #include "stats_log_util.h"
38 #include "stats_util.h"
39 #include "statslog_statsd.h"
40 #include "utils/DbUtils.h"
41 #include "utils/api_tracing.h"
42
43 using android::util::FIELD_COUNT_REPEATED;
44 using android::util::FIELD_TYPE_INT32;
45 using android::util::FIELD_TYPE_INT64;
46 using android::util::FIELD_TYPE_MESSAGE;
47 using android::util::FIELD_TYPE_STRING;
48 using android::util::ProtoOutputStream;
49
50 using std::set;
51 using std::string;
52 using std::unique_ptr;
53 using std::vector;
54
55 namespace android {
56 namespace os {
57 namespace statsd {
58
59 const int FIELD_ID_METRICS = 1;
60 const int FIELD_ID_ANNOTATIONS = 7;
61 const int FIELD_ID_ANNOTATIONS_INT64 = 1;
62 const int FIELD_ID_ANNOTATIONS_INT32 = 2;
63
64 // for ActiveConfig
65 const int FIELD_ID_ACTIVE_CONFIG_ID = 1;
66 const int FIELD_ID_ACTIVE_CONFIG_UID = 2;
67 const int FIELD_ID_ACTIVE_CONFIG_METRIC = 3;
68
MetricsManager(const ConfigKey & key,const StatsdConfig & config,const int64_t timeBaseNs,const int64_t currentTimeNs,const sp<UidMap> & uidMap,const sp<StatsPullerManager> & pullerManager,const sp<AlarmMonitor> & anomalyAlarmMonitor,const sp<AlarmMonitor> & periodicAlarmMonitor)69 MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config,
70 const int64_t timeBaseNs, const int64_t currentTimeNs,
71 const sp<UidMap>& uidMap,
72 const sp<StatsPullerManager>& pullerManager,
73 const sp<AlarmMonitor>& anomalyAlarmMonitor,
74 const sp<AlarmMonitor>& periodicAlarmMonitor)
75 : mConfigKey(key),
76 mUidMap(uidMap),
77 mPackageCertificateHashSizeBytes(
78 static_cast<uint8_t>(config.package_certificate_hash_size_bytes())),
79 mTtlNs(config.has_ttl_in_seconds() ? config.ttl_in_seconds() * NS_PER_SEC : -1),
80 mTtlEndNs(-1),
81 mLastReportTimeNs(currentTimeNs),
82 mLastReportWallClockNs(getWallClockNs()),
83 mPullerManager(pullerManager),
84 mWhitelistedAtomIds(config.whitelisted_atom_ids().begin(),
85 config.whitelisted_atom_ids().end()),
86 mShouldPersistHistory(config.persist_locally()),
87 mUseV2SoftMemoryCalculation(config.statsd_config_options().use_v2_soft_memory_limit()),
88 mOmitSystemUidsInUidMap(config.statsd_config_options().omit_system_uids_in_uidmap()) {
89 if (!isAtLeastU() && config.has_restricted_metrics_delegate_package_name()) {
90 mInvalidConfigReason =
91 InvalidConfigReason(INVALID_CONFIG_REASON_RESTRICTED_METRIC_NOT_ENABLED);
92 return;
93 }
94 if (config.has_restricted_metrics_delegate_package_name()) {
95 mRestrictedMetricsDelegatePackageName = config.restricted_metrics_delegate_package_name();
96 }
97 // Init the ttl end timestamp.
98 refreshTtl(timeBaseNs);
99 mInvalidConfigReason = initStatsdConfig(
100 key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
101 timeBaseNs, currentTimeNs, this, mTagIdsToMatchersMap, mAllAtomMatchingTrackers,
102 mAtomMatchingTrackerMap, mAllConditionTrackers, mConditionTrackerMap,
103 mAllMetricProducers, mMetricProducerMap, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers,
104 mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap,
105 mActivationAtomTrackerToMetricMap, mDeactivationAtomTrackerToMetricMap,
106 mAlertTrackerMap, mMetricIndexesWithActivation, mStateProtoHashes, mNoReportMetricIds);
107
108 mHashStringsInReport = config.hash_strings_in_metric_report();
109 mVersionStringsInReport = config.version_strings_in_metric_report();
110 mInstallerInReport = config.installer_in_metric_report();
111
112 createAllLogSourcesFromConfig(config);
113 setMaxMetricsBytesFromConfig(config);
114 setTriggerGetDataBytesFromConfig(config);
115 mPullerManager->RegisterPullUidProvider(mConfigKey, this);
116
117 // Store the sub-configs used.
118 for (const auto& annotation : config.annotation()) {
119 mAnnotations.emplace_back(annotation.field_int64(), annotation.field_int32());
120 }
121 verifyGuardrailsAndUpdateStatsdStats();
122 initializeConfigActiveStatus();
123 }
124
~MetricsManager()125 MetricsManager::~MetricsManager() {
126 for (auto it : mAllMetricProducers) {
127 for (int atomId : it->getSlicedStateAtoms()) {
128 StateManager::getInstance().unregisterListener(atomId, it);
129 }
130 }
131 mPullerManager->UnregisterPullUidProvider(mConfigKey, this);
132
133 VLOG("~MetricsManager()");
134 }
135
updateConfig(const StatsdConfig & config,const int64_t timeBaseNs,const int64_t currentTimeNs,const sp<AlarmMonitor> & anomalyAlarmMonitor,const sp<AlarmMonitor> & periodicAlarmMonitor)136 bool MetricsManager::updateConfig(const StatsdConfig& config, const int64_t timeBaseNs,
137 const int64_t currentTimeNs,
138 const sp<AlarmMonitor>& anomalyAlarmMonitor,
139 const sp<AlarmMonitor>& periodicAlarmMonitor) {
140 if (!isAtLeastU() && config.has_restricted_metrics_delegate_package_name()) {
141 mInvalidConfigReason =
142 InvalidConfigReason(INVALID_CONFIG_REASON_RESTRICTED_METRIC_NOT_ENABLED);
143 return false;
144 }
145 if (config.has_restricted_metrics_delegate_package_name()) {
146 mRestrictedMetricsDelegatePackageName = config.restricted_metrics_delegate_package_name();
147 } else {
148 mRestrictedMetricsDelegatePackageName = nullopt;
149 }
150 vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers;
151 unordered_map<int64_t, int> newAtomMatchingTrackerMap;
152 vector<sp<ConditionTracker>> newConditionTrackers;
153 unordered_map<int64_t, int> newConditionTrackerMap;
154 map<int64_t, uint64_t> newStateProtoHashes;
155 vector<sp<MetricProducer>> newMetricProducers;
156 unordered_map<int64_t, int> newMetricProducerMap;
157 vector<sp<AnomalyTracker>> newAnomalyTrackers;
158 unordered_map<int64_t, int> newAlertTrackerMap;
159 vector<sp<AlarmTracker>> newPeriodicAlarmTrackers;
160 mTagIdsToMatchersMap.clear();
161 mConditionToMetricMap.clear();
162 mTrackerToMetricMap.clear();
163 mTrackerToConditionMap.clear();
164 mActivationAtomTrackerToMetricMap.clear();
165 mDeactivationAtomTrackerToMetricMap.clear();
166 mMetricIndexesWithActivation.clear();
167 mNoReportMetricIds.clear();
168 mInvalidConfigReason = updateStatsdConfig(
169 mConfigKey, config, mUidMap, mPullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
170 timeBaseNs, currentTimeNs, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap,
171 mAllConditionTrackers, mConditionTrackerMap, mAllMetricProducers, mMetricProducerMap,
172 mAllAnomalyTrackers, mAlertTrackerMap, mStateProtoHashes, this, mTagIdsToMatchersMap,
173 newAtomMatchingTrackers, newAtomMatchingTrackerMap, newConditionTrackers,
174 newConditionTrackerMap, newMetricProducers, newMetricProducerMap, newAnomalyTrackers,
175 newAlertTrackerMap, newPeriodicAlarmTrackers, mConditionToMetricMap,
176 mTrackerToMetricMap, mTrackerToConditionMap, mActivationAtomTrackerToMetricMap,
177 mDeactivationAtomTrackerToMetricMap, mMetricIndexesWithActivation, newStateProtoHashes,
178 mNoReportMetricIds);
179 mAllAtomMatchingTrackers = newAtomMatchingTrackers;
180 mAtomMatchingTrackerMap = newAtomMatchingTrackerMap;
181 mAllConditionTrackers = newConditionTrackers;
182 mConditionTrackerMap = newConditionTrackerMap;
183 mAllMetricProducers = newMetricProducers;
184 mMetricProducerMap = newMetricProducerMap;
185 mStateProtoHashes = newStateProtoHashes;
186 mAllAnomalyTrackers = newAnomalyTrackers;
187 mAlertTrackerMap = newAlertTrackerMap;
188 mAllPeriodicAlarmTrackers = newPeriodicAlarmTrackers;
189
190 mTtlNs = config.has_ttl_in_seconds() ? config.ttl_in_seconds() * NS_PER_SEC : -1;
191 refreshTtl(currentTimeNs);
192
193 mHashStringsInReport = config.hash_strings_in_metric_report();
194 mVersionStringsInReport = config.version_strings_in_metric_report();
195 mInstallerInReport = config.installer_in_metric_report();
196 mWhitelistedAtomIds.clear();
197 mWhitelistedAtomIds.insert(config.whitelisted_atom_ids().begin(),
198 config.whitelisted_atom_ids().end());
199 mShouldPersistHistory = config.persist_locally();
200 mPackageCertificateHashSizeBytes = config.package_certificate_hash_size_bytes();
201 mUseV2SoftMemoryCalculation = config.statsd_config_options().use_v2_soft_memory_limit();
202 mOmitSystemUidsInUidMap = config.statsd_config_options().omit_system_uids_in_uidmap();
203
204 // Store the sub-configs used.
205 mAnnotations.clear();
206 for (const auto& annotation : config.annotation()) {
207 mAnnotations.emplace_back(annotation.field_int64(), annotation.field_int32());
208 }
209
210 mAllowedUid.clear();
211 mAllowedPkg.clear();
212 mDefaultPullUids.clear();
213 mPullAtomUids.clear();
214 mPullAtomPackages.clear();
215 createAllLogSourcesFromConfig(config);
216 setMaxMetricsBytesFromConfig(config);
217 setTriggerGetDataBytesFromConfig(config);
218
219 verifyGuardrailsAndUpdateStatsdStats();
220 initializeConfigActiveStatus();
221 return !mInvalidConfigReason.has_value();
222 }
223
createAllLogSourcesFromConfig(const StatsdConfig & config)224 void MetricsManager::createAllLogSourcesFromConfig(const StatsdConfig& config) {
225 // Init allowed pushed atom uids.
226 for (const auto& source : config.allowed_log_source()) {
227 auto it = UidMap::sAidToUidMapping.find(source);
228 if (it != UidMap::sAidToUidMapping.end()) {
229 mAllowedUid.push_back(it->second);
230 } else {
231 mAllowedPkg.push_back(source);
232 }
233 }
234
235 if (mAllowedUid.size() + mAllowedPkg.size() > StatsdStats::kMaxLogSourceCount) {
236 ALOGE("Too many log sources. This is likely to be an error in the config.");
237 mInvalidConfigReason = InvalidConfigReason(INVALID_CONFIG_REASON_TOO_MANY_LOG_SOURCES);
238 } else {
239 initAllowedLogSources();
240 }
241
242 // Init default allowed pull atom uids.
243 int numPullPackages = 0;
244 for (const string& pullSource : config.default_pull_packages()) {
245 auto it = UidMap::sAidToUidMapping.find(pullSource);
246 if (it != UidMap::sAidToUidMapping.end()) {
247 numPullPackages++;
248 mDefaultPullUids.insert(it->second);
249 } else {
250 ALOGE("Default pull atom packages must be in sAidToUidMapping");
251 mInvalidConfigReason =
252 InvalidConfigReason(INVALID_CONFIG_REASON_DEFAULT_PULL_PACKAGES_NOT_IN_MAP);
253 }
254 }
255 // Init per-atom pull atom packages.
256 for (const PullAtomPackages& pullAtomPackages : config.pull_atom_packages()) {
257 int32_t atomId = pullAtomPackages.atom_id();
258 for (const string& pullPackage : pullAtomPackages.packages()) {
259 numPullPackages++;
260 auto it = UidMap::sAidToUidMapping.find(pullPackage);
261 if (it != UidMap::sAidToUidMapping.end()) {
262 mPullAtomUids[atomId].insert(it->second);
263 } else {
264 mPullAtomPackages[atomId].insert(pullPackage);
265 }
266 }
267 }
268 if (numPullPackages > StatsdStats::kMaxPullAtomPackages) {
269 ALOGE("Too many sources in default_pull_packages and pull_atom_packages. This is likely to "
270 "be an error in the config");
271 mInvalidConfigReason =
272 InvalidConfigReason(INVALID_CONFIG_REASON_TOO_MANY_SOURCES_IN_PULL_PACKAGES);
273 } else {
274 initPullAtomSources();
275 }
276 }
277
setMaxMetricsBytesFromConfig(const StatsdConfig & config)278 void MetricsManager::setMaxMetricsBytesFromConfig(const StatsdConfig& config) {
279 if (!config.has_max_metrics_memory_kb()) {
280 mMaxMetricsBytes = StatsdStats::kDefaultMaxMetricsBytesPerConfig;
281 return;
282 }
283 if (config.max_metrics_memory_kb() <= 0 ||
284 static_cast<size_t>(config.max_metrics_memory_kb() * 1024) >
285 StatsdStats::kHardMaxMetricsBytesPerConfig) {
286 ALOGW("Memory limit must be between 0KB and 20MB. Setting to default value (2MB).");
287 mMaxMetricsBytes = StatsdStats::kDefaultMaxMetricsBytesPerConfig;
288 } else {
289 mMaxMetricsBytes = config.max_metrics_memory_kb() * 1024;
290 }
291 }
292
setTriggerGetDataBytesFromConfig(const StatsdConfig & config)293 void MetricsManager::setTriggerGetDataBytesFromConfig(const StatsdConfig& config) {
294 if (!config.has_soft_metrics_memory_kb()) {
295 mTriggerGetDataBytes = StatsdStats::kDefaultBytesPerConfigTriggerGetData;
296 return;
297 }
298 if (config.soft_metrics_memory_kb() <= 0 ||
299 static_cast<size_t>(config.soft_metrics_memory_kb() * 1024) >
300 StatsdStats::kHardMaxTriggerGetDataBytes) {
301 ALOGW("Memory limit ust be between 0KB and 10MB. Setting to default value (192KB).");
302 mTriggerGetDataBytes = StatsdStats::kDefaultBytesPerConfigTriggerGetData;
303 } else {
304 mTriggerGetDataBytes = config.soft_metrics_memory_kb() * 1024;
305 }
306 }
307
verifyGuardrailsAndUpdateStatsdStats()308 void MetricsManager::verifyGuardrailsAndUpdateStatsdStats() {
309 // Guardrail. Reject the config if it's too big.
310 if (mAllMetricProducers.size() > StatsdStats::kMaxMetricCountPerConfig) {
311 ALOGE("This config has too many metrics! Reject!");
312 mInvalidConfigReason = InvalidConfigReason(INVALID_CONFIG_REASON_TOO_MANY_METRICS);
313 }
314 if (mAllConditionTrackers.size() > StatsdStats::kMaxConditionCountPerConfig) {
315 ALOGE("This config has too many predicates! Reject!");
316 mInvalidConfigReason = InvalidConfigReason(INVALID_CONFIG_REASON_TOO_MANY_CONDITIONS);
317 }
318 if (mAllAtomMatchingTrackers.size() > StatsdStats::kMaxMatcherCountPerConfig) {
319 ALOGE("This config has too many matchers! Reject!");
320 mInvalidConfigReason = InvalidConfigReason(INVALID_CONFIG_REASON_TOO_MANY_MATCHERS);
321 }
322 if (mAllAnomalyTrackers.size() > StatsdStats::kMaxAlertCountPerConfig) {
323 ALOGE("This config has too many alerts! Reject!");
324 mInvalidConfigReason = InvalidConfigReason(INVALID_CONFIG_REASON_TOO_MANY_ALERTS);
325 }
326 // no matter whether this config is valid, log it in the stats.
327 StatsdStats::getInstance().noteConfigReceived(
328 mConfigKey, mAllMetricProducers.size(), mAllConditionTrackers.size(),
329 mAllAtomMatchingTrackers.size(), mAllAnomalyTrackers.size(), mAnnotations,
330 mInvalidConfigReason);
331 }
332
initializeConfigActiveStatus()333 void MetricsManager::initializeConfigActiveStatus() {
334 mIsAlwaysActive = (mMetricIndexesWithActivation.size() != mAllMetricProducers.size()) ||
335 (mAllMetricProducers.size() == 0);
336 mIsActive = mIsAlwaysActive;
337 for (int metric : mMetricIndexesWithActivation) {
338 mIsActive |= mAllMetricProducers[metric]->isActive();
339 }
340 VLOG("mIsActive is initialized to %d", mIsActive);
341 }
342
initAllowedLogSources()343 void MetricsManager::initAllowedLogSources() {
344 std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
345 mAllowedLogSources.clear();
346 mAllowedLogSources.insert(mAllowedUid.begin(), mAllowedUid.end());
347
348 for (const auto& pkg : mAllowedPkg) {
349 auto uids = mUidMap->getAppUid(pkg);
350 mAllowedLogSources.insert(uids.begin(), uids.end());
351 }
352 if (STATSD_DEBUG) {
353 for (const auto& uid : mAllowedLogSources) {
354 VLOG("Allowed uid %d", uid);
355 }
356 }
357 }
358
initPullAtomSources()359 void MetricsManager::initPullAtomSources() {
360 std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
361 mCombinedPullAtomUids.clear();
362 for (const auto& [atomId, uids] : mPullAtomUids) {
363 mCombinedPullAtomUids[atomId].insert(uids.begin(), uids.end());
364 }
365 for (const auto& [atomId, packages] : mPullAtomPackages) {
366 for (const string& pkg : packages) {
367 set<int32_t> uids = mUidMap->getAppUid(pkg);
368 mCombinedPullAtomUids[atomId].insert(uids.begin(), uids.end());
369 }
370 }
371 }
372
isConfigValid() const373 bool MetricsManager::isConfigValid() const {
374 return !mInvalidConfigReason.has_value();
375 }
376
notifyAppUpgrade(const int64_t eventTimeNs,const string & apk,const int uid,const int64_t version)377 void MetricsManager::notifyAppUpgrade(const int64_t eventTimeNs, const string& apk, const int uid,
378 const int64_t version) {
379 // Inform all metric producers.
380 for (const auto& it : mAllMetricProducers) {
381 it->notifyAppUpgrade(eventTimeNs);
382 }
383 // check if we care this package
384 if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) {
385 // We will re-initialize the whole list because we don't want to keep the multi mapping of
386 // UID<->pkg inside MetricsManager to reduce the memory usage.
387 initAllowedLogSources();
388 }
389
390 for (const auto& it : mPullAtomPackages) {
391 if (it.second.find(apk) != it.second.end()) {
392 initPullAtomSources();
393 return;
394 }
395 }
396 }
397
notifyAppRemoved(const int64_t eventTimeNs,const string & apk,const int uid)398 void MetricsManager::notifyAppRemoved(const int64_t eventTimeNs, const string& apk, const int uid) {
399 // Inform all metric producers.
400 for (const auto& it : mAllMetricProducers) {
401 it->notifyAppRemoved(eventTimeNs);
402 }
403 // check if we care this package
404 if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) {
405 // We will re-initialize the whole list because we don't want to keep the multi mapping of
406 // UID<->pkg inside MetricsManager to reduce the memory usage.
407 initAllowedLogSources();
408 }
409
410 for (const auto& it : mPullAtomPackages) {
411 if (it.second.find(apk) != it.second.end()) {
412 initPullAtomSources();
413 return;
414 }
415 }
416 }
417
onUidMapReceived(const int64_t eventTimeNs)418 void MetricsManager::onUidMapReceived(const int64_t eventTimeNs) {
419 // Purposefully don't inform metric producers on a new snapshot
420 // because we don't need to flush partial buckets.
421 // This occurs if a new user is added/removed or statsd crashes.
422 initPullAtomSources();
423
424 if (mAllowedPkg.size() == 0) {
425 return;
426 }
427 initAllowedLogSources();
428 }
429
onStatsdInitCompleted(const int64_t eventTimeNs)430 void MetricsManager::onStatsdInitCompleted(const int64_t eventTimeNs) {
431 ATRACE_CALL();
432 // Inform all metric producers.
433 for (const auto& it : mAllMetricProducers) {
434 it->onStatsdInitCompleted(eventTimeNs);
435 }
436 }
437
init()438 void MetricsManager::init() {
439 for (const auto& producer : mAllMetricProducers) {
440 producer->prepareFirstBucket();
441 }
442 }
443
getPullAtomUids(int32_t atomId)444 vector<int32_t> MetricsManager::getPullAtomUids(int32_t atomId) {
445 std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
446 vector<int32_t> uids;
447 const auto& it = mCombinedPullAtomUids.find(atomId);
448 if (it != mCombinedPullAtomUids.end()) {
449 uids.insert(uids.end(), it->second.begin(), it->second.end());
450 }
451 uids.insert(uids.end(), mDefaultPullUids.begin(), mDefaultPullUids.end());
452 return uids;
453 }
454
useV2SoftMemoryCalculation()455 bool MetricsManager::useV2SoftMemoryCalculation() {
456 return mUseV2SoftMemoryCalculation;
457 }
458
dumpStates(int out,bool verbose)459 void MetricsManager::dumpStates(int out, bool verbose) {
460 dprintf(out, "ConfigKey %s, allowed source:", mConfigKey.ToString().c_str());
461 {
462 std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
463 for (const auto& source : mAllowedLogSources) {
464 dprintf(out, "%d ", source);
465 }
466 }
467 dprintf(out, "\n");
468 for (const auto& producer : mAllMetricProducers) {
469 producer->dumpStates(out, verbose);
470 }
471 }
472
dropData(const int64_t dropTimeNs)473 void MetricsManager::dropData(const int64_t dropTimeNs) {
474 for (const auto& producer : mAllMetricProducers) {
475 producer->dropData(dropTimeNs);
476 }
477 }
478
onDumpReport(const int64_t dumpTimeStampNs,const int64_t wallClockNs,const bool include_current_partial_bucket,const bool erase_data,const DumpLatency dumpLatency,std::set<string> * str_set,ProtoOutputStream * protoOutput)479 void MetricsManager::onDumpReport(const int64_t dumpTimeStampNs, const int64_t wallClockNs,
480 const bool include_current_partial_bucket, const bool erase_data,
481 const DumpLatency dumpLatency, std::set<string>* str_set,
482 ProtoOutputStream* protoOutput) {
483 if (hasRestrictedMetricsDelegate()) {
484 // TODO(b/268150038): report error to statsdstats
485 VLOG("Unexpected call to onDumpReport in restricted metricsmanager.");
486 return;
487 }
488
489 vector<std::pair<int32_t, int32_t>> queueOverflowStats =
490 StatsdStats::getInstance().getQueueOverflowAtomsStats();
491 processQueueOverflowStats(queueOverflowStats);
492
493 VLOG("=========================Metric Reports Start==========================");
494 // one StatsLogReport per MetricProduer
495 for (const auto& producer : mAllMetricProducers) {
496 if (mNoReportMetricIds.find(producer->getMetricId()) == mNoReportMetricIds.end()) {
497 uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
498 FIELD_ID_METRICS);
499 if (mHashStringsInReport) {
500 producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, erase_data,
501 dumpLatency, str_set, protoOutput);
502 } else {
503 producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, erase_data,
504 dumpLatency, nullptr, protoOutput);
505 }
506 protoOutput->end(token);
507 } else {
508 producer->clearPastBuckets(dumpTimeStampNs);
509 }
510 }
511 for (const auto& annotation : mAnnotations) {
512 uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
513 FIELD_ID_ANNOTATIONS);
514 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ANNOTATIONS_INT64,
515 (long long)annotation.first);
516 protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_ANNOTATIONS_INT32, annotation.second);
517 protoOutput->end(token);
518 }
519
520 // Do not update the timestamps when data is not cleared to avoid timestamps from being
521 // misaligned.
522 if (erase_data) {
523 mLastReportTimeNs = dumpTimeStampNs;
524 mLastReportWallClockNs = wallClockNs;
525 }
526 VLOG("=========================Metric Reports End==========================");
527 }
528
checkLogCredentials(const int32_t uid,const int32_t atomId) const529 bool MetricsManager::checkLogCredentials(const int32_t uid, const int32_t atomId) const {
530 if (mWhitelistedAtomIds.find(atomId) != mWhitelistedAtomIds.end()) {
531 return true;
532 }
533
534 if (uid == AID_ROOT || (uid >= AID_SYSTEM && uid < AID_SHELL)) {
535 // enable atoms logged from pre-installed Android system services
536 return true;
537 }
538
539 std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
540 if (mAllowedLogSources.find(uid) == mAllowedLogSources.end()) {
541 VLOG("log source %d not on the whitelist", uid);
542 return false;
543 }
544 return true;
545 }
546
547 // Consume the stats log if it's interesting to this metric.
onLogEvent(const LogEvent & event)548 void MetricsManager::onLogEvent(const LogEvent& event) {
549 if (!isConfigValid()) {
550 return;
551 }
552
553 const int tagId = event.GetTagId();
554
555 if (tagId == util::STATS_SOCKET_LOSS_REPORTED) {
556 // Hard coded logic to handle socket loss info to highlight metric corruption reason
557 // STATS_SOCKET_LOSS_REPORTED might not be part of atoms allow list - but some of lost
558 // atoms can be always allowed - that is the reason to evaluate SocketLossInfo content prior
559 // the checkLogCredentials below
560 const std::optional<SocketLossInfo>& lossInfo = toSocketLossInfo(event);
561 if (lossInfo) {
562 onLogEventLost(*lossInfo);
563 }
564 // next, atom is going to be propagated to be consumed by metrics if any
565 }
566
567 if (!checkLogCredentials(event)) {
568 return;
569 }
570
571 const int64_t eventTimeNs = event.GetElapsedTimestampNs();
572
573 bool isActive = mIsAlwaysActive;
574
575 // Set of metrics that are still active after flushing.
576 unordered_set<int> activeMetricsIndices;
577
578 // Update state of all metrics w/ activation conditions as of eventTimeNs.
579 for (int metricIndex : mMetricIndexesWithActivation) {
580 const sp<MetricProducer>& metric = mAllMetricProducers[metricIndex];
581 metric->flushIfExpire(eventTimeNs);
582 if (metric->isActive()) {
583 // If this metric w/ activation condition is still active after
584 // flushing, remember it.
585 activeMetricsIndices.insert(metricIndex);
586 }
587 }
588
589 mIsActive = isActive || !activeMetricsIndices.empty();
590
591 const auto matchersIt = mTagIdsToMatchersMap.find(tagId);
592
593 if (matchersIt == mTagIdsToMatchersMap.end()) {
594 // Not interesting...
595 return;
596 }
597
598 if (event.isParsedHeaderOnly()) {
599 // This should not happen if metric config is defined for certain atom id
600 const int64_t firstMatcherId =
601 mAllAtomMatchingTrackers[*matchersIt->second.begin()]->getId();
602 ALOGW("Atom %d is mistakenly skipped - there is a matcher %lld for it", tagId,
603 (long long)firstMatcherId);
604 return;
605 }
606
607 vector<MatchingState> matcherCache(mAllAtomMatchingTrackers.size(),
608 MatchingState::kNotComputed);
609 vector<shared_ptr<LogEvent>> matcherTransformations(matcherCache.size(), nullptr);
610
611 for (const auto& matcherIndex : matchersIt->second) {
612 mAllAtomMatchingTrackers[matcherIndex]->onLogEvent(event, matcherIndex,
613 mAllAtomMatchingTrackers, matcherCache,
614 matcherTransformations);
615 }
616
617 // Set of metrics that received an activation cancellation.
618 unordered_set<int> metricIndicesWithCanceledActivations;
619
620 // Determine which metric activations received a cancellation and cancel them.
621 for (const auto& it : mDeactivationAtomTrackerToMetricMap) {
622 if (matcherCache[it.first] == MatchingState::kMatched) {
623 for (int metricIndex : it.second) {
624 mAllMetricProducers[metricIndex]->cancelEventActivation(it.first);
625 metricIndicesWithCanceledActivations.insert(metricIndex);
626 }
627 }
628 }
629
630 // Determine whether any metrics are no longer active after cancelling metric activations.
631 for (const int metricIndex : metricIndicesWithCanceledActivations) {
632 const sp<MetricProducer>& metric = mAllMetricProducers[metricIndex];
633 metric->flushIfExpire(eventTimeNs);
634 if (!metric->isActive()) {
635 activeMetricsIndices.erase(metricIndex);
636 }
637 }
638
639 isActive |= !activeMetricsIndices.empty();
640
641 // Determine which metric activations should be turned on and turn them on
642 for (const auto& it : mActivationAtomTrackerToMetricMap) {
643 if (matcherCache[it.first] == MatchingState::kMatched) {
644 for (int metricIndex : it.second) {
645 mAllMetricProducers[metricIndex]->activate(it.first, eventTimeNs);
646 isActive |= mAllMetricProducers[metricIndex]->isActive();
647 }
648 }
649 }
650
651 mIsActive = isActive;
652
653 // A bitmap to see which ConditionTracker needs to be re-evaluated.
654 vector<uint8_t> conditionToBeEvaluated(mAllConditionTrackers.size(), false);
655 vector<shared_ptr<LogEvent>> conditionToTransformedLogEvents(mAllConditionTrackers.size(),
656 nullptr);
657
658 for (const auto& [matcherIndex, conditionList] : mTrackerToConditionMap) {
659 if (matcherCache[matcherIndex] == MatchingState::kMatched) {
660 for (const int conditionIndex : conditionList) {
661 conditionToBeEvaluated[conditionIndex] = true;
662 conditionToTransformedLogEvents[conditionIndex] =
663 matcherTransformations[matcherIndex];
664 }
665 }
666 }
667
668 vector<ConditionState> conditionCache(mAllConditionTrackers.size(),
669 ConditionState::kNotEvaluated);
670 // A bitmap to track if a condition has changed value.
671 vector<uint8_t> changedCache(mAllConditionTrackers.size(), false);
672 for (size_t i = 0; i < mAllConditionTrackers.size(); i++) {
673 if (!conditionToBeEvaluated[i]) {
674 continue;
675 }
676 sp<ConditionTracker>& condition = mAllConditionTrackers[i];
677 const LogEvent& conditionEvent = conditionToTransformedLogEvents[i] == nullptr
678 ? event
679 : *conditionToTransformedLogEvents[i];
680 condition->evaluateCondition(conditionEvent, matcherCache, mAllConditionTrackers,
681 conditionCache, changedCache);
682 }
683
684 for (size_t i = 0; i < mAllConditionTrackers.size(); i++) {
685 if (!changedCache[i]) {
686 continue;
687 }
688 auto it = mConditionToMetricMap.find(i);
689 if (it == mConditionToMetricMap.end()) {
690 continue;
691 }
692 auto& metricList = it->second;
693 for (auto metricIndex : metricList) {
694 // Metric cares about non sliced condition, and it's changed.
695 // Push the new condition to it directly.
696 if (!mAllMetricProducers[metricIndex]->isConditionSliced()) {
697 mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i],
698 eventTimeNs);
699 // Metric cares about sliced conditions, and it may have changed. Send
700 // notification, and the metric can query the sliced conditions that are
701 // interesting to it.
702 } else {
703 mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(conditionCache[i],
704 eventTimeNs);
705 }
706 }
707 }
708 // For matched AtomMatchers, tell relevant metrics that a matched event has come.
709 for (size_t i = 0; i < mAllAtomMatchingTrackers.size(); i++) {
710 if (matcherCache[i] == MatchingState::kMatched) {
711 StatsdStats::getInstance().noteMatcherMatched(mConfigKey,
712 mAllAtomMatchingTrackers[i]->getId());
713 auto it = mTrackerToMetricMap.find(i);
714 if (it == mTrackerToMetricMap.end()) {
715 continue;
716 }
717 auto& metricList = it->second;
718 const LogEvent& metricEvent =
719 matcherTransformations[i] == nullptr ? event : *matcherTransformations[i];
720 for (const int metricIndex : metricList) {
721 // pushed metrics are never scheduled pulls
722 mAllMetricProducers[metricIndex]->onMatchedLogEvent(i, metricEvent);
723 }
724 }
725 }
726 }
727
onLogEventLost(const SocketLossInfo & socketLossInfo)728 void MetricsManager::onLogEventLost(const SocketLossInfo& socketLossInfo) {
729 // socketLossInfo stores atomId per UID - to eliminate duplicates using set
730 const set<int> uniqueLostAtomIds(socketLossInfo.atomIds.begin(), socketLossInfo.atomIds.end());
731
732 // pass lost atom id to all relevant metrics
733 for (const auto lostAtomId : uniqueLostAtomIds) {
734 /**
735 * Socket loss atom:
736 * - comes from a specific uid (originUid)
737 * - specifies the uid in the atom payload (socketLossInfo.uid)
738 * - provides a list of atom ids that are lost
739 *
740 * For atom id that is lost (lostAtomId below):
741 * - if that atom id is allowed from any uid, then always count this atom as lost
742 * - else, if the originUid (from ucred) (socketLossInfo.uid below and is the same for all
743 * uniqueLostAtomIds) is in the allowed log sources - count this atom as lost
744 */
745
746 if (!checkLogCredentials(socketLossInfo.uid, lostAtomId)) {
747 continue;
748 }
749
750 notifyMetricsAboutLostAtom(lostAtomId, DATA_CORRUPTED_SOCKET_LOSS);
751 }
752 }
753
notifyMetricsAboutLostAtom(int32_t lostAtomId,DataCorruptedReason reason)754 int MetricsManager::notifyMetricsAboutLostAtom(int32_t lostAtomId, DataCorruptedReason reason) {
755 const auto matchersIt = mTagIdsToMatchersMap.find(lostAtomId);
756 if (matchersIt == mTagIdsToMatchersMap.end()) {
757 // atom is lost - but no metrics in config reference it
758 return 0;
759 }
760 int numberOfNotifiedMetrics = 0;
761
762 const auto& matchersIndexesListForLostAtom = matchersIt->second;
763 for (const auto matcherIndex : matchersIndexesListForLostAtom) {
764 // look through any metric which depends on matcher
765 auto metricMapIt = mTrackerToMetricMap.find(matcherIndex);
766 if (metricMapIt != mTrackerToMetricMap.end()) {
767 const auto& metricsList = metricMapIt->second;
768 for (const int metricIndex : metricsList) {
769 mAllMetricProducers[metricIndex]->onMatchedLogEventLost(
770 lostAtomId, reason, MetricProducer::LostAtomType::kWhat);
771 numberOfNotifiedMetrics++;
772 }
773 }
774
775 // look through any condition tracker which depends on matcher
776 const auto conditionMapIt = mTrackerToConditionMap.find(matcherIndex);
777 if (conditionMapIt != mTrackerToConditionMap.end()) {
778 const auto& conditionTrackersList = conditionMapIt->second;
779 for (const int conditionTrackerIndex : conditionTrackersList) {
780 metricMapIt = mConditionToMetricMap.find(conditionTrackerIndex);
781 if (metricMapIt != mConditionToMetricMap.end()) {
782 const auto& metricsList = metricMapIt->second;
783 for (const int metricIndex : metricsList) {
784 mAllMetricProducers[metricIndex]->onMatchedLogEventLost(
785 lostAtomId, reason, MetricProducer::LostAtomType::kCondition);
786 numberOfNotifiedMetrics++;
787 }
788 }
789 }
790 }
791 }
792 return numberOfNotifiedMetrics;
793 }
794
onAnomalyAlarmFired(const int64_t timestampNs,unordered_set<sp<const InternalAlarm>,SpHash<InternalAlarm>> & alarmSet)795 void MetricsManager::onAnomalyAlarmFired(
796 const int64_t timestampNs,
797 unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) {
798 for (const auto& itr : mAllAnomalyTrackers) {
799 itr->informAlarmsFired(timestampNs, alarmSet);
800 }
801 }
802
onPeriodicAlarmFired(const int64_t timestampNs,unordered_set<sp<const InternalAlarm>,SpHash<InternalAlarm>> & alarmSet)803 void MetricsManager::onPeriodicAlarmFired(
804 const int64_t timestampNs,
805 unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) {
806 for (const auto& itr : mAllPeriodicAlarmTrackers) {
807 itr->informAlarmsFired(timestampNs, alarmSet);
808 }
809 }
810
811 // Returns the total byte size of all metrics managed by a single config source.
byteSize()812 size_t MetricsManager::byteSize() {
813 size_t totalSize = 0;
814 for (const auto& metricProducer : mAllMetricProducers) {
815 totalSize += metricProducer->byteSize();
816 }
817 return totalSize;
818 }
819
loadActiveConfig(const ActiveConfig & config,int64_t currentTimeNs)820 void MetricsManager::loadActiveConfig(const ActiveConfig& config, int64_t currentTimeNs) {
821 if (config.metric_size() == 0) {
822 ALOGW("No active metric for config %s", mConfigKey.ToString().c_str());
823 return;
824 }
825
826 for (int i = 0; i < config.metric_size(); i++) {
827 const auto& activeMetric = config.metric(i);
828 for (int metricIndex : mMetricIndexesWithActivation) {
829 const auto& metric = mAllMetricProducers[metricIndex];
830 if (metric->getMetricId() == activeMetric.id()) {
831 VLOG("Setting active metric: %lld", (long long)metric->getMetricId());
832 metric->loadActiveMetric(activeMetric, currentTimeNs);
833 if (!mIsActive && metric->isActive()) {
834 StatsdStats::getInstance().noteActiveStatusChanged(mConfigKey,
835 /*activate=*/true);
836 }
837 mIsActive |= metric->isActive();
838 }
839 }
840 }
841 }
842
writeActiveConfigToProtoOutputStream(int64_t currentTimeNs,const DumpReportReason reason,ProtoOutputStream * proto)843 void MetricsManager::writeActiveConfigToProtoOutputStream(int64_t currentTimeNs,
844 const DumpReportReason reason,
845 ProtoOutputStream* proto) {
846 proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_CONFIG_ID, (long long)mConfigKey.GetId());
847 proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_CONFIG_UID, mConfigKey.GetUid());
848 for (int metricIndex : mMetricIndexesWithActivation) {
849 const auto& metric = mAllMetricProducers[metricIndex];
850 const uint64_t metricToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
851 FIELD_ID_ACTIVE_CONFIG_METRIC);
852 metric->writeActiveMetricToProtoOutputStream(currentTimeNs, reason, proto);
853 proto->end(metricToken);
854 }
855 }
856
writeMetadataToProto(int64_t currentWallClockTimeNs,int64_t systemElapsedTimeNs,metadata::StatsMetadata * statsMetadata)857 bool MetricsManager::writeMetadataToProto(int64_t currentWallClockTimeNs,
858 int64_t systemElapsedTimeNs,
859 metadata::StatsMetadata* statsMetadata) {
860 bool metadataWritten = false;
861 metadata::ConfigKey* configKey = statsMetadata->mutable_config_key();
862 configKey->set_config_id(mConfigKey.GetId());
863 configKey->set_uid(mConfigKey.GetUid());
864 for (const auto& anomalyTracker : mAllAnomalyTrackers) {
865 metadata::AlertMetadata* alertMetadata = statsMetadata->add_alert_metadata();
866 bool alertWritten = anomalyTracker->writeAlertMetadataToProto(
867 currentWallClockTimeNs, systemElapsedTimeNs, alertMetadata);
868 if (!alertWritten) {
869 statsMetadata->mutable_alert_metadata()->RemoveLast();
870 }
871 metadataWritten |= alertWritten;
872 }
873
874 for (const auto& metricProducer : mAllMetricProducers) {
875 metadata::MetricMetadata* metricMetadata = statsMetadata->add_metric_metadata();
876 bool metricWritten = metricProducer->writeMetricMetadataToProto(metricMetadata);
877 if (!metricWritten) {
878 statsMetadata->mutable_metric_metadata()->RemoveLast();
879 }
880 metadataWritten |= metricWritten;
881 }
882 return metadataWritten;
883 }
884
loadMetadata(const metadata::StatsMetadata & metadata,int64_t currentWallClockTimeNs,int64_t systemElapsedTimeNs)885 void MetricsManager::loadMetadata(const metadata::StatsMetadata& metadata,
886 int64_t currentWallClockTimeNs, int64_t systemElapsedTimeNs) {
887 for (const metadata::AlertMetadata& alertMetadata : metadata.alert_metadata()) {
888 int64_t alertId = alertMetadata.alert_id();
889 const auto& it = mAlertTrackerMap.find(alertId);
890 if (it == mAlertTrackerMap.end()) {
891 ALOGE("No anomalyTracker found for alertId %lld", (long long)alertId);
892 continue;
893 }
894 mAllAnomalyTrackers[it->second]->loadAlertMetadata(alertMetadata, currentWallClockTimeNs,
895 systemElapsedTimeNs);
896 }
897 for (const metadata::MetricMetadata& metricMetadata : metadata.metric_metadata()) {
898 int64_t metricId = metricMetadata.metric_id();
899 const auto& it = mMetricProducerMap.find(metricId);
900 if (it == mMetricProducerMap.end()) {
901 ALOGE("No metricProducer found for metricId %lld", (long long)metricId);
902 }
903 mAllMetricProducers[it->second]->loadMetricMetadataFromProto(metricMetadata);
904 }
905 }
906
enforceRestrictedDataTtls(const int64_t wallClockNs)907 void MetricsManager::enforceRestrictedDataTtls(const int64_t wallClockNs) {
908 if (!hasRestrictedMetricsDelegate()) {
909 return;
910 }
911 sqlite3* db = dbutils::getDb(mConfigKey);
912 if (db == nullptr) {
913 ALOGE("Failed to open sqlite db");
914 dbutils::closeDb(db);
915 return;
916 }
917 for (const auto& producer : mAllMetricProducers) {
918 producer->enforceRestrictedDataTtl(db, wallClockNs);
919 }
920 dbutils::closeDb(db);
921 }
922
validateRestrictedMetricsDelegate(const int32_t callingUid)923 bool MetricsManager::validateRestrictedMetricsDelegate(const int32_t callingUid) {
924 if (!hasRestrictedMetricsDelegate()) {
925 return false;
926 }
927
928 set<int32_t> possibleUids = mUidMap->getAppUid(mRestrictedMetricsDelegatePackageName.value());
929
930 return possibleUids.find(callingUid) != possibleUids.end();
931 }
932
flushRestrictedData()933 void MetricsManager::flushRestrictedData() {
934 if (!hasRestrictedMetricsDelegate()) {
935 return;
936 }
937 int64_t flushStartNs = getElapsedRealtimeNs();
938 for (const auto& producer : mAllMetricProducers) {
939 producer->flushRestrictedData();
940 }
941 StatsdStats::getInstance().noteRestrictedConfigFlushLatency(
942 mConfigKey, getElapsedRealtimeNs() - flushStartNs);
943 }
944
getAllMetricIds() const945 vector<int64_t> MetricsManager::getAllMetricIds() const {
946 vector<int64_t> metricIds;
947 metricIds.reserve(mMetricProducerMap.size());
948 for (const auto& [metricId, _] : mMetricProducerMap) {
949 metricIds.push_back(metricId);
950 }
951 return metricIds;
952 }
953
addAllAtomIds(LogEventFilter::AtomIdSet & allIds) const954 void MetricsManager::addAllAtomIds(LogEventFilter::AtomIdSet& allIds) const {
955 for (const auto& [atomId, _] : mTagIdsToMatchersMap) {
956 allIds.insert(atomId);
957 }
958 }
959
processQueueOverflowStats(const StatsdStats::QueueOverflowAtomsStats & overflowStats)960 void MetricsManager::processQueueOverflowStats(
961 const StatsdStats::QueueOverflowAtomsStats& overflowStats) {
962 assert((overflowStats.size() < mQueueOverflowAtomsStats.size()) &&
963 "StatsdStats reset unexpected");
964
965 for (const auto [atomId, count] : overflowStats) {
966 // are there new atoms dropped due to queue overflow since previous dumpReport request
967 auto droppedAtomStatsIt = mQueueOverflowAtomsStats.find(atomId);
968 if (droppedAtomStatsIt != mQueueOverflowAtomsStats.end() &&
969 droppedAtomStatsIt->second == count) {
970 // no new dropped atoms detected for the atomId
971 continue;
972 }
973
974 if (notifyMetricsAboutLostAtom(atomId, DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW) > 0) {
975 // there is at least one metric interested in the lost atom, keep track of it
976 // to update it again only if there will be more dropped atoms
977 mQueueOverflowAtomsStats[atomId] = count;
978 } else {
979 // there are no metrics interested in dropped atom
980 if (droppedAtomStatsIt != mQueueOverflowAtomsStats.end()) {
981 // but there were metrics which are interested in the atom and now they are removed
982 mQueueOverflowAtomsStats.erase(droppedAtomStatsIt);
983 }
984 }
985 }
986 }
987
988 } // namespace statsd
989 } // namespace os
990 } // namespace android
991