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 
17 #define STATSD_DEBUG false
18 
19 #include "Log.h"
20 
21 #include "DurationMetricProducer.h"
22 
23 #include <limits.h>
24 #include <stdlib.h>
25 
26 #include "guardrail/StatsdStats.h"
27 #include "metrics/parsing_utils/metrics_manager_util.h"
28 #include "stats_log_util.h"
29 #include "stats_util.h"
30 
31 using android::util::FIELD_COUNT_REPEATED;
32 using android::util::FIELD_TYPE_BOOL;
33 using android::util::FIELD_TYPE_FLOAT;
34 using android::util::FIELD_TYPE_INT32;
35 using android::util::FIELD_TYPE_INT64;
36 using android::util::FIELD_TYPE_MESSAGE;
37 using android::util::FIELD_TYPE_STRING;
38 using android::util::ProtoOutputStream;
39 using std::string;
40 using std::unordered_map;
41 using std::vector;
42 using std::shared_ptr;
43 
44 namespace android {
45 namespace os {
46 namespace statsd {
47 
48 // for StatsLogReport
49 const int FIELD_ID_ID = 1;
50 const int FIELD_ID_DURATION_METRICS = 6;
51 const int FIELD_ID_TIME_BASE = 9;
52 const int FIELD_ID_BUCKET_SIZE = 10;
53 const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
54 const int FIELD_ID_IS_ACTIVE = 14;
55 // for DurationMetricDataWrapper
56 const int FIELD_ID_DATA = 1;
57 // for DurationMetricData
58 const int FIELD_ID_DIMENSION_IN_WHAT = 1;
59 const int FIELD_ID_BUCKET_INFO = 3;
60 const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
61 const int FIELD_ID_SLICE_BY_STATE = 6;
62 // for DurationBucketInfo
63 const int FIELD_ID_DURATION = 3;
64 const int FIELD_ID_BUCKET_NUM = 4;
65 const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
66 const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
67 const int FIELD_ID_CONDITION_TRUE_NS = 7;
68 
DurationMetricProducer(const ConfigKey & key,const DurationMetric & metric,const int conditionIndex,const vector<ConditionState> & initialConditionCache,const int whatIndex,const int startIndex,const int stopIndex,const int stopAllIndex,const bool nesting,const sp<ConditionWizard> & wizard,const uint64_t protoHash,const FieldMatcher & internalDimensions,const int64_t timeBaseNs,const int64_t startTimeNs,const unordered_map<int,shared_ptr<Activation>> & eventActivationMap,const unordered_map<int,vector<shared_ptr<Activation>>> & eventDeactivationMap,const vector<int> & slicedStateAtoms,const unordered_map<int,unordered_map<int,int64_t>> & stateGroupMap)69 DurationMetricProducer::DurationMetricProducer(
70         const ConfigKey& key, const DurationMetric& metric, const int conditionIndex,
71         const vector<ConditionState>& initialConditionCache, const int whatIndex,
72         const int startIndex, const int stopIndex, const int stopAllIndex, const bool nesting,
73         const sp<ConditionWizard>& wizard, const uint64_t protoHash,
74         const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs,
75         const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
76         const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
77         const vector<int>& slicedStateAtoms,
78         const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
79     : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard,
80                      protoHash, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
81                      stateGroupMap, getAppUpgradeBucketSplit(metric)),
82       mAggregationType(metric.aggregation_type()),
83       mStartIndex(startIndex),
84       mStopIndex(stopIndex),
85       mStopAllIndex(stopAllIndex),
86       mNested(nesting),
87       mContainANYPositionInInternalDimensions(false) {
88     if (metric.has_bucket()) {
89         mBucketSizeNs =
90                 TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
91     } else {
92         mBucketSizeNs = LLONG_MAX;
93     }
94 
95     if (metric.has_threshold()) {
96         mUploadThreshold = metric.threshold();
97     }
98 
99     if (metric.has_dimensions_in_what()) {
100         translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat);
101         mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
102     }
103 
104     if (internalDimensions.has_field()) {
105         translateFieldMatcher(internalDimensions, &mInternalDimensions);
106         mContainANYPositionInInternalDimensions = HasPositionANY(internalDimensions);
107     }
108     if (mContainANYPositionInInternalDimensions) {
109         ALOGE("Position ANY in internal dimension not supported.");
110     }
111     if (mContainANYPositionInDimensionsInWhat) {
112         ALOGE("Position ANY in dimension_in_what not supported.");
113     }
114 
115     // Dimensions in what must be subset of internal dimensions
116     if (!subsetDimensions(mDimensionsInWhat, mInternalDimensions)) {
117         ALOGE("Dimensions in what must be a subset of the internal dimensions");
118         // TODO: Add invalidConfigReason
119         mValid = false;
120     }
121 
122     mShouldUseNestedDimensions = ShouldUseNestedDimensions(metric.dimensions_in_what());
123 
124     if (metric.links().size() > 0) {
125         for (const auto& link : metric.links()) {
126             Metric2Condition mc;
127             mc.conditionId = link.condition();
128             translateFieldMatcher(link.fields_in_what(), &mc.metricFields);
129             translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
130             if (!subsetDimensions(mc.metricFields, mInternalDimensions)) {
131                 ALOGE(("Condition links must be a subset of the internal dimensions"));
132                 // TODO: Add invalidConfigReason
133                 mValid = false;
134             }
135             mMetric2ConditionLinks.push_back(mc);
136         }
137         mConditionSliced = true;
138     }
139     mUnSlicedPartCondition = ConditionState::kUnknown;
140 
141     for (const auto& stateLink : metric.state_link()) {
142         Metric2State ms;
143         ms.stateAtomId = stateLink.state_atom_id();
144         translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields);
145         translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields);
146         if (!subsetDimensions(ms.metricFields, mInternalDimensions)) {
147             ALOGE(("State links must be a subset of the dimensions in what  internal dimensions"));
148             // TODO: Add invalidConfigReason
149             mValid = false;
150         }
151         mMetric2StateLinks.push_back(ms);
152     }
153 
154     mUseWhatDimensionAsInternalDimension = equalDimensions(mDimensionsInWhat, mInternalDimensions);
155     if (mWizard != nullptr && mConditionTrackerIndex >= 0 &&
156             mMetric2ConditionLinks.size() == 1) {
157         mHasLinksToAllConditionDimensionsInTracker = mWizard->equalOutputDimensions(
158                 mConditionTrackerIndex, mMetric2ConditionLinks.begin()->conditionFields);
159     }
160     flushIfNeededLocked(startTimeNs);
161     // Adjust start for partial bucket
162     mCurrentBucketStartTimeNs = startTimeNs;
163     VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)mMetricId,
164          (long long)mBucketSizeNs, (long long)mTimeBaseNs);
165 
166     initTrueDimensions(whatIndex, startTimeNs);
167     mConditionTimer.newBucketStart(mCurrentBucketStartTimeNs, mCurrentBucketStartTimeNs);
168     mConditionTimer.onConditionChanged(mIsActive && mCondition == ConditionState::kTrue,
169                                        mCurrentBucketStartTimeNs);
170 }
171 
~DurationMetricProducer()172 DurationMetricProducer::~DurationMetricProducer() {
173     VLOG("~DurationMetric() called");
174 }
175 
onConfigUpdatedLocked(const StatsdConfig & config,const int configIndex,const int metricIndex,const vector<sp<AtomMatchingTracker>> & allAtomMatchingTrackers,const unordered_map<int64_t,int> & oldAtomMatchingTrackerMap,const unordered_map<int64_t,int> & newAtomMatchingTrackerMap,const sp<EventMatcherWizard> & matcherWizard,const vector<sp<ConditionTracker>> & allConditionTrackers,const unordered_map<int64_t,int> & conditionTrackerMap,const sp<ConditionWizard> & wizard,const unordered_map<int64_t,int> & metricToActivationMap,unordered_map<int,vector<int>> & trackerToMetricMap,unordered_map<int,vector<int>> & conditionToMetricMap,unordered_map<int,vector<int>> & activationAtomTrackerToMetricMap,unordered_map<int,vector<int>> & deactivationAtomTrackerToMetricMap,vector<int> & metricsWithActivation)176 optional<InvalidConfigReason> DurationMetricProducer::onConfigUpdatedLocked(
177         const StatsdConfig& config, const int configIndex, const int metricIndex,
178         const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
179         const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
180         const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
181         const sp<EventMatcherWizard>& matcherWizard,
182         const vector<sp<ConditionTracker>>& allConditionTrackers,
183         const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
184         const unordered_map<int64_t, int>& metricToActivationMap,
185         unordered_map<int, vector<int>>& trackerToMetricMap,
186         unordered_map<int, vector<int>>& conditionToMetricMap,
187         unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
188         unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
189         vector<int>& metricsWithActivation) {
190     optional<InvalidConfigReason> invalidConfigReason = MetricProducer::onConfigUpdatedLocked(
191             config, configIndex, metricIndex, allAtomMatchingTrackers, oldAtomMatchingTrackerMap,
192             newAtomMatchingTrackerMap, matcherWizard, allConditionTrackers, conditionTrackerMap,
193             wizard, metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
194             activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
195             metricsWithActivation);
196     if (invalidConfigReason.has_value()) {
197         return invalidConfigReason;
198     }
199 
200     const DurationMetric& metric = config.duration_metric(configIndex);
201     const auto& what_it = conditionTrackerMap.find(metric.what());
202     if (what_it == conditionTrackerMap.end()) {
203         ALOGE("DurationMetric's \"what\" is not present in the config");
204         return createInvalidConfigReasonWithPredicate(
205                 INVALID_CONFIG_REASON_DURATION_METRIC_WHAT_NOT_FOUND, mMetricId, metric.what());
206     }
207 
208     const Predicate& durationWhat = config.predicate(what_it->second);
209     if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) {
210         ALOGE("DurationMetric's \"what\" must be a simple condition");
211         return createInvalidConfigReasonWithPredicate(
212                 INVALID_CONFIG_REASON_DURATION_METRIC_WHAT_NOT_SIMPLE, mMetricId, metric.what());
213     }
214 
215     const SimplePredicate& simplePredicate = durationWhat.simple_predicate();
216 
217     // Update indices: mStartIndex, mStopIndex, mStopAllIndex, mConditionIndex and MetricsManager
218     // maps.
219     invalidConfigReason = handleMetricWithAtomMatchingTrackers(
220             simplePredicate.start(), mMetricId, metricIndex, metric.has_dimensions_in_what(),
221             allAtomMatchingTrackers, newAtomMatchingTrackerMap, trackerToMetricMap, mStartIndex);
222     if (invalidConfigReason.has_value()) {
223         ALOGE("Duration metrics must specify a valid start event matcher");
224         return invalidConfigReason;
225     }
226 
227     if (simplePredicate.has_stop()) {
228         invalidConfigReason = handleMetricWithAtomMatchingTrackers(
229                 simplePredicate.stop(), mMetricId, metricIndex, metric.has_dimensions_in_what(),
230                 allAtomMatchingTrackers, newAtomMatchingTrackerMap, trackerToMetricMap, mStopIndex);
231         if (invalidConfigReason.has_value()) {
232             return invalidConfigReason;
233         }
234     }
235 
236     if (simplePredicate.has_stop_all()) {
237         invalidConfigReason = handleMetricWithAtomMatchingTrackers(
238                 simplePredicate.stop_all(), mMetricId, metricIndex, metric.has_dimensions_in_what(),
239                 allAtomMatchingTrackers, newAtomMatchingTrackerMap, trackerToMetricMap,
240                 mStopAllIndex);
241         if (invalidConfigReason.has_value()) {
242             return invalidConfigReason;
243         }
244     }
245 
246     if (metric.has_condition()) {
247         invalidConfigReason = handleMetricWithConditions(
248                 metric.condition(), mMetricId, metricIndex, conditionTrackerMap, metric.links(),
249                 allConditionTrackers, mConditionTrackerIndex, conditionToMetricMap);
250         if (invalidConfigReason.has_value()) {
251             return invalidConfigReason;
252         }
253     }
254 
255     for (const auto& it : mCurrentSlicedDurationTrackerMap) {
256         it.second->onConfigUpdated(wizard, mConditionTrackerIndex);
257     }
258 
259     return nullopt;
260 }
261 
initTrueDimensions(const int whatIndex,const int64_t startTimeNs)262 void DurationMetricProducer::initTrueDimensions(const int whatIndex, const int64_t startTimeNs) {
263     std::lock_guard<std::mutex> lock(mMutex);
264     // Currently whatIndex will only be -1 in tests. In the future, we might want to avoid creating
265     // a ConditionTracker if the condition is only used in the "what" of a duration metric. In that
266     // scenario, -1 can also be passed.
267     if (whatIndex == -1) {
268         return;
269     }
270     const map<HashableDimensionKey, int>* slicedWhatMap = mWizard->getSlicedDimensionMap(whatIndex);
271     for (const auto& [internalDimKey, count] : *slicedWhatMap) {
272         for (int i = 0; i < count; i++) {
273             // Fake start events.
274             handleMatchedLogEventValuesLocked(mStartIndex, internalDimKey.getValues(), startTimeNs);
275         }
276     }
277 }
278 
addAnomalyTracker(const Alert & alert,const sp<AlarmMonitor> & anomalyAlarmMonitor,const UpdateStatus & updateStatus,const int64_t updateTimeNs)279 sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker(
280         const Alert& alert, const sp<AlarmMonitor>& anomalyAlarmMonitor,
281         const UpdateStatus& updateStatus, const int64_t updateTimeNs) {
282     std::lock_guard<std::mutex> lock(mMutex);
283     if (mAggregationType == DurationMetric_AggregationType_SUM) {
284         if (alert.trigger_if_sum_gt() > alert.num_buckets() * mBucketSizeNs) {
285             ALOGW("invalid alert for SUM: threshold (%f) > possible recordable value (%d x %lld)",
286                   alert.trigger_if_sum_gt(), alert.num_buckets(), (long long)mBucketSizeNs);
287             return nullptr;
288         }
289     }
290     sp<AnomalyTracker> anomalyTracker =
291             new DurationAnomalyTracker(alert, mConfigKey, anomalyAlarmMonitor);
292     // The update status is either new or replaced.
293     addAnomalyTrackerLocked(anomalyTracker, updateStatus, updateTimeNs);
294     return anomalyTracker;
295 }
296 
297 // Adds an AnomalyTracker that has already been created.
298 // Note: this gets called on config updates, and will only get called if the metric and the
299 // associated alert are preserved, which means the AnomalyTracker must be a DurationAnomalyTracker.
addAnomalyTracker(sp<AnomalyTracker> & anomalyTracker,const int64_t updateTimeNs)300 void DurationMetricProducer::addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker,
301                                                const int64_t updateTimeNs) {
302     std::lock_guard<std::mutex> lock(mMutex);
303     addAnomalyTrackerLocked(anomalyTracker, UpdateStatus::UPDATE_PRESERVE, updateTimeNs);
304 }
305 
addAnomalyTrackerLocked(sp<AnomalyTracker> & anomalyTracker,const UpdateStatus & updateStatus,const int64_t updateTimeNs)306 void DurationMetricProducer::addAnomalyTrackerLocked(sp<AnomalyTracker>& anomalyTracker,
307                                                      const UpdateStatus& updateStatus,
308                                                      const int64_t updateTimeNs) {
309     mAnomalyTrackers.push_back(anomalyTracker);
310     for (const auto& [_, durationTracker] : mCurrentSlicedDurationTrackerMap) {
311         durationTracker->addAnomalyTracker(anomalyTracker, updateStatus, updateTimeNs);
312     }
313 }
onStateChanged(const int64_t eventTimeNs,const int32_t atomId,const HashableDimensionKey & primaryKey,const FieldValue & oldState,const FieldValue & newState)314 void DurationMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
315                                             const HashableDimensionKey& primaryKey,
316                                             const FieldValue& oldState,
317                                             const FieldValue& newState) {
318     // Check if this metric has a StateMap. If so, map the new state value to
319     // the correct state group id.
320     FieldValue newStateCopy = newState;
321     mapStateValue(atomId, &newStateCopy);
322 
323     flushIfNeededLocked(eventTimeNs);
324 
325     // Each duration tracker is mapped to a different whatKey (a set of values from the
326     // dimensionsInWhat fields). We notify all trackers iff the primaryKey field values from the
327     // state change event are a subset of the tracker's whatKey field values.
328     //
329     // Ex. For a duration metric dimensioned on uid and tag:
330     // DurationTracker1 whatKey = uid: 1001, tag: 1
331     // DurationTracker2 whatKey = uid: 1002, tag 1
332     //
333     // If the state change primaryKey = uid: 1001, we only notify DurationTracker1 of a state
334     // change.
335     for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
336         if (!containsLinkedStateValues(whatIt.first, primaryKey, mMetric2StateLinks, atomId)) {
337             continue;
338         }
339         whatIt.second->onStateChanged(eventTimeNs, atomId, newStateCopy);
340     }
341 }
342 
createDurationTracker(const MetricDimensionKey & eventKey) const343 unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
344         const MetricDimensionKey& eventKey) const {
345     switch (mAggregationType) {
346         case DurationMetric_AggregationType_SUM:
347             return make_unique<OringDurationTracker>(
348                     mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
349                     mCurrentBucketStartTimeNs, mCurrentBucketNum, mTimeBaseNs, mBucketSizeNs,
350                     mConditionSliced, mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
351         case DurationMetric_AggregationType_MAX_SPARSE:
352             return make_unique<MaxDurationTracker>(
353                     mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
354                     mCurrentBucketStartTimeNs, mCurrentBucketNum, mTimeBaseNs, mBucketSizeNs,
355                     mConditionSliced, mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
356     }
357 }
358 
359 // SlicedConditionChange optimization case 1:
360 // 1. If combination condition, logical operation is AND, only one sliced child predicate.
361 // 2. The links covers all dimension fields in the sliced child condition predicate.
onSlicedConditionMayChangeLocked_opt1(const int64_t eventTime)362 void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(const int64_t eventTime) {
363     if (mMetric2ConditionLinks.size() != 1 ||
364         !mHasLinksToAllConditionDimensionsInTracker) {
365         return;
366     }
367 
368     bool  currentUnSlicedPartCondition = true;
369     if (!mWizard->IsSimpleCondition(mConditionTrackerIndex)) {
370         ConditionState unslicedPartState =
371             mWizard->getUnSlicedPartConditionState(mConditionTrackerIndex);
372         // When the unsliced part is still false, return directly.
373         if (mUnSlicedPartCondition == ConditionState::kFalse &&
374             unslicedPartState == ConditionState::kFalse) {
375             return;
376         }
377         mUnSlicedPartCondition = unslicedPartState;
378         currentUnSlicedPartCondition = mUnSlicedPartCondition > 0;
379     }
380 
381     auto dimensionsChangedToTrue = mWizard->getChangedToTrueDimensions(mConditionTrackerIndex);
382     auto dimensionsChangedToFalse = mWizard->getChangedToFalseDimensions(mConditionTrackerIndex);
383 
384     // The condition change is from the unsliced predicates.
385     // We need to find out the true dimensions from the sliced predicate and flip their condition
386     // state based on the new unsliced condition state.
387     if (dimensionsChangedToTrue == nullptr || dimensionsChangedToFalse == nullptr ||
388         (dimensionsChangedToTrue->empty() && dimensionsChangedToFalse->empty())) {
389         const map<HashableDimensionKey, int>* slicedConditionMap =
390                 mWizard->getSlicedDimensionMap(mConditionTrackerIndex);
391         for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
392             HashableDimensionKey linkedConditionDimensionKey;
393             getDimensionForCondition(whatIt.first.getValues(), mMetric2ConditionLinks[0],
394                                      &linkedConditionDimensionKey);
395             const auto& slicedConditionIt = slicedConditionMap->find(linkedConditionDimensionKey);
396             if (slicedConditionIt != slicedConditionMap->end() && slicedConditionIt->second > 0) {
397                 whatIt.second->onConditionChanged(currentUnSlicedPartCondition, eventTime);
398             }
399         }
400     } else {
401         // Handle the condition change from the sliced predicate.
402         if (currentUnSlicedPartCondition) {
403             for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
404                 HashableDimensionKey linkedConditionDimensionKey;
405                 getDimensionForCondition(whatIt.first.getValues(), mMetric2ConditionLinks[0],
406                                          &linkedConditionDimensionKey);
407                 if (dimensionsChangedToTrue->find(linkedConditionDimensionKey) !=
408                         dimensionsChangedToTrue->end()) {
409                     whatIt.second->onConditionChanged(true, eventTime);
410                 }
411                 if (dimensionsChangedToFalse->find(linkedConditionDimensionKey) !=
412                         dimensionsChangedToFalse->end()) {
413                     whatIt.second->onConditionChanged(false, eventTime);
414                 }
415             }
416         }
417     }
418 }
419 
onSlicedConditionMayChangeInternalLocked(const int64_t eventTimeNs)420 void DurationMetricProducer::onSlicedConditionMayChangeInternalLocked(const int64_t eventTimeNs) {
421     bool changeDimTrackable = mWizard->IsChangedDimensionTrackable(mConditionTrackerIndex);
422     if (changeDimTrackable && mHasLinksToAllConditionDimensionsInTracker) {
423         onSlicedConditionMayChangeLocked_opt1(eventTimeNs);
424         return;
425     }
426 
427     // Now for each of the on-going event, check if the condition has changed for them.
428     for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
429         whatIt.second->onSlicedConditionMayChange(eventTimeNs);
430     }
431 }
432 
onSlicedConditionMayChangeLocked(bool overallCondition,const int64_t eventTime)433 void DurationMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
434                                                               const int64_t eventTime) {
435     VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
436 
437     if (!mIsActive) {
438         return;
439     }
440 
441     flushIfNeededLocked(eventTime);
442 
443     if (!mConditionSliced) {
444         return;
445     }
446 
447     onSlicedConditionMayChangeInternalLocked(eventTime);
448 }
449 
onActiveStateChangedLocked(const int64_t eventTimeNs,const bool isActive)450 void DurationMetricProducer::onActiveStateChangedLocked(const int64_t eventTimeNs,
451                                                         const bool isActive) {
452     MetricProducer::onActiveStateChangedLocked(eventTimeNs, isActive);
453 
454     if (!mConditionSliced) {
455         if (ConditionState::kTrue != mCondition) {
456             return;
457         }
458 
459         if (isActive) {
460             flushIfNeededLocked(eventTimeNs);
461         }
462 
463         for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
464             whatIt.second->onConditionChanged(isActive, eventTimeNs);
465         }
466         mConditionTimer.onConditionChanged(isActive, eventTimeNs);
467     } else if (isActive) {
468         flushIfNeededLocked(eventTimeNs);
469         onSlicedConditionMayChangeInternalLocked(eventTimeNs);
470     } else {  // mConditionSliced == true && !isActive
471         for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
472             whatIt.second->onConditionChanged(isActive, eventTimeNs);
473         }
474     }
475 }
476 
onConditionChangedLocked(const bool conditionMet,const int64_t eventTime)477 void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet,
478                                                       const int64_t eventTime) {
479     VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
480     mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
481 
482     if (!mIsActive) {
483         return;
484     }
485 
486     flushIfNeededLocked(eventTime);
487     for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
488         whatIt.second->onConditionChanged(conditionMet, eventTime);
489     }
490 
491     mConditionTimer.onConditionChanged(mCondition, eventTime);
492 }
493 
dropDataLocked(const int64_t dropTimeNs)494 void DurationMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
495     flushIfNeededLocked(dropTimeNs);
496     StatsdStats::getInstance().noteBucketDropped(mMetricId);
497     mPastBuckets.clear();
498 }
499 
clearPastBucketsLocked(const int64_t dumpTimeNs)500 void DurationMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
501     flushIfNeededLocked(dumpTimeNs);
502     mPastBuckets.clear();
503 }
504 
onDumpReportLocked(const int64_t dumpTimeNs,const bool include_current_partial_bucket,const bool erase_data,const DumpLatency dumpLatency,std::set<string> * str_set,ProtoOutputStream * protoOutput)505 void DurationMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
506                                                 const bool include_current_partial_bucket,
507                                                 const bool erase_data,
508                                                 const DumpLatency dumpLatency,
509                                                 std::set<string> *str_set,
510                                                 ProtoOutputStream* protoOutput) {
511     if (include_current_partial_bucket) {
512         flushLocked(dumpTimeNs);
513     } else {
514         flushIfNeededLocked(dumpTimeNs);
515     }
516     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
517     protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
518 
519     if (mPastBuckets.empty()) {
520         VLOG(" Duration metric, empty return");
521         return;
522     }
523 
524     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
525     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
526 
527     if (!mShouldUseNestedDimensions) {
528         if (!mDimensionsInWhat.empty()) {
529             uint64_t dimenPathToken = protoOutput->start(
530                     FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT);
531             writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
532             protoOutput->end(dimenPathToken);
533         }
534     }
535 
536     uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS);
537 
538     VLOG("Duration metric %lld dump report now...", (long long)mMetricId);
539 
540     for (const auto& pair : mPastBuckets) {
541         const MetricDimensionKey& dimensionKey = pair.first;
542         VLOG("  dimension key %s", dimensionKey.toString().c_str());
543 
544         uint64_t wrapperToken =
545                 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
546 
547         // First fill dimension.
548         if (mShouldUseNestedDimensions) {
549             uint64_t dimensionToken = protoOutput->start(
550                     FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
551             writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
552             protoOutput->end(dimensionToken);
553         } else {
554             writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
555                                            FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
556         }
557         // Then fill slice_by_state.
558         for (auto state : dimensionKey.getStateValuesKey().getValues()) {
559             uint64_t stateToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
560                                                      FIELD_ID_SLICE_BY_STATE);
561             writeStateToProto(state, protoOutput);
562             protoOutput->end(stateToken);
563         }
564         // Then fill bucket_info (DurationBucketInfo).
565         for (const auto& bucket : pair.second) {
566             uint64_t bucketInfoToken = protoOutput->start(
567                     FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
568             if (bucket.mBucketEndNs - bucket.mBucketStartNs != mBucketSizeNs) {
569                 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_MILLIS,
570                                    (long long)NanoToMillis(bucket.mBucketStartNs));
571                 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_MILLIS,
572                                    (long long)NanoToMillis(bucket.mBucketEndNs));
573             } else {
574                 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
575                                    (long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
576             }
577             protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DURATION, (long long)bucket.mDuration);
578 
579             // We only write the condition timer value if the metric has a
580             // condition and isn't sliced by state or condition.
581             // TODO(b/268531762): Slice the condition timer by state and condition
582             if (mConditionTrackerIndex >= 0 && mSlicedStateAtoms.empty() && !mConditionSliced) {
583                 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_TRUE_NS,
584                                    (long long)bucket.mConditionTrueNs);
585             }
586 
587             protoOutput->end(bucketInfoToken);
588             VLOG("\t bucket [%lld - %lld] duration: %lld", (long long)bucket.mBucketStartNs,
589                  (long long)bucket.mBucketEndNs, (long long)bucket.mDuration);
590         }
591 
592         protoOutput->end(wrapperToken);
593     }
594 
595     protoOutput->end(protoToken);
596     if (erase_data) {
597         mPastBuckets.clear();
598     }
599 }
600 
flushIfNeededLocked(const int64_t & eventTimeNs)601 void DurationMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
602     int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
603 
604     if (currentBucketEndTimeNs > eventTimeNs) {
605         return;
606     }
607     VLOG("flushing...........");
608     int numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
609     int64_t nextBucketNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
610     flushCurrentBucketLocked(eventTimeNs, nextBucketNs);
611 
612     mCurrentBucketNum += numBucketsForward;
613 }
614 
flushCurrentBucketLocked(const int64_t & eventTimeNs,const int64_t & nextBucketStartTimeNs)615 void DurationMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
616                                                       const int64_t& nextBucketStartTimeNs) {
617     const auto [globalConditionTrueNs, globalConditionCorrectionNs] =
618             mConditionTimer.newBucketStart(eventTimeNs, nextBucketStartTimeNs);
619 
620     for (auto whatIt = mCurrentSlicedDurationTrackerMap.begin();
621             whatIt != mCurrentSlicedDurationTrackerMap.end();) {
622         if (whatIt->second->flushCurrentBucket(eventTimeNs, mUploadThreshold, globalConditionTrueNs,
623                                                &mPastBuckets)) {
624             VLOG("erase bucket for key %s", whatIt->first.toString().c_str());
625             whatIt = mCurrentSlicedDurationTrackerMap.erase(whatIt);
626         } else {
627             ++whatIt;
628         }
629     }
630 
631     StatsdStats::getInstance().noteBucketCount(mMetricId);
632     mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
633     // Reset mHasHitGuardrail boolean since bucket was reset
634     mHasHitGuardrail = false;
635 }
636 
dumpStatesLocked(FILE * out,bool verbose) const637 void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
638     if (mCurrentSlicedDurationTrackerMap.size() == 0) {
639         return;
640     }
641 
642     fprintf(out, "DurationMetric %lld dimension size %lu\n", (long long)mMetricId,
643             (unsigned long)mCurrentSlicedDurationTrackerMap.size());
644     if (verbose) {
645         for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) {
646             fprintf(out, "\t(what)%s\n", whatIt.first.toString().c_str());
647             whatIt.second->dumpStates(out, verbose);
648         }
649     }
650 }
651 
hitGuardRailLocked(const MetricDimensionKey & newKey)652 bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
653     auto whatIt = mCurrentSlicedDurationTrackerMap.find(newKey.getDimensionKeyInWhat());
654     if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
655         // 1. Report the tuple count if the tuple count > soft limit
656         if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
657             size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1;
658             StatsdStats::getInstance().noteMetricDimensionSize(
659                     mConfigKey, mMetricId, newTupleCount);
660             // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
661             if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
662                 if (!mHasHitGuardrail) {
663                     ALOGE("DurationMetric %lld dropping data for what dimension key %s",
664                           (long long)mMetricId, newKey.getDimensionKeyInWhat().toString().c_str());
665                     mHasHitGuardrail = true;
666                 }
667                 StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId);
668                 return true;
669             }
670         }
671     }
672     return false;
673 }
674 
handleStartEvent(const MetricDimensionKey & eventKey,const ConditionKey & conditionKeys,bool condition,const int64_t eventTimeNs,const vector<FieldValue> & eventValues)675 void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey,
676                                               const ConditionKey& conditionKeys, bool condition,
677                                               const int64_t eventTimeNs,
678                                               const vector<FieldValue>& eventValues) {
679     const auto& whatKey = eventKey.getDimensionKeyInWhat();
680     auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey);
681     if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
682         if (hitGuardRailLocked(eventKey)) {
683             return;
684         }
685         mCurrentSlicedDurationTrackerMap[whatKey] = createDurationTracker(eventKey);
686     }
687 
688     auto it = mCurrentSlicedDurationTrackerMap.find(whatKey);
689     if (mUseWhatDimensionAsInternalDimension) {
690         it->second->noteStart(whatKey, condition, eventTimeNs, conditionKeys);
691         return;
692     }
693 
694     if (mInternalDimensions.empty()) {
695         it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, eventTimeNs, conditionKeys);
696     } else {
697         HashableDimensionKey dimensionKey = DEFAULT_DIMENSION_KEY;
698         filterValues(mInternalDimensions, eventValues, &dimensionKey);
699         it->second->noteStart(dimensionKey, condition, eventTimeNs, conditionKeys);
700     }
701 }
702 
onMatchedLogEventInternalLocked(const size_t matcherIndex,const MetricDimensionKey & eventKey,const ConditionKey & conditionKeys,bool condition,const LogEvent & event,const map<int,HashableDimensionKey> & statePrimaryKeys)703 void DurationMetricProducer::onMatchedLogEventInternalLocked(
704         const size_t matcherIndex, const MetricDimensionKey& eventKey,
705         const ConditionKey& conditionKeys, bool condition, const LogEvent& event,
706         const map<int, HashableDimensionKey>& statePrimaryKeys) {
707     ALOGW("Not used in duration tracker.");
708 }
709 
onMatchedLogEventLocked(const size_t matcherIndex,const LogEvent & event)710 void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
711                                                      const LogEvent& event) {
712     handleMatchedLogEventValuesLocked(matcherIndex, event.getValues(),
713                                       event.GetElapsedTimestampNs());
714 }
715 
handleMatchedLogEventValuesLocked(const size_t matcherIndex,const vector<FieldValue> & values,const int64_t eventTimeNs)716 void DurationMetricProducer::handleMatchedLogEventValuesLocked(const size_t matcherIndex,
717                                                                const vector<FieldValue>& values,
718                                                                const int64_t eventTimeNs) {
719     if (eventTimeNs < mTimeBaseNs) {
720         return;
721     }
722 
723     if (mIsActive) {
724         flushIfNeededLocked(eventTimeNs);
725     }
726 
727     // Handles Stopall events.
728     if ((int)matcherIndex == mStopAllIndex) {
729         for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
730             whatIt.second->noteStopAll(eventTimeNs);
731         }
732         return;
733     }
734 
735     if (!passesSampleCheckLocked(values)) {
736         return;
737     }
738 
739     HashableDimensionKey dimensionInWhat = DEFAULT_DIMENSION_KEY;
740     if (!mDimensionsInWhat.empty()) {
741         filterValues(mDimensionsInWhat, values, &dimensionInWhat);
742     }
743 
744     // Stores atom id to primary key pairs for each state atom that the metric is
745     // sliced by.
746     std::map<int, HashableDimensionKey> statePrimaryKeys;
747 
748     // For states with primary fields, use MetricStateLinks to get the primary
749     // field values from the log event. These values will form a primary key
750     // that will be used to query StateTracker for the correct state value.
751     for (const auto& stateLink : mMetric2StateLinks) {
752         getDimensionForState(values, stateLink, &statePrimaryKeys[stateLink.stateAtomId]);
753     }
754 
755     // For each sliced state, query StateTracker for the state value using
756     // either the primary key from the previous step or the DEFAULT_DIMENSION_KEY.
757     //
758     // Expected functionality: for any case where the MetricStateLinks are
759     // initialized incorrectly (ex. # of state links != # of primary fields, no
760     // links are provided for a state with primary fields, links are provided
761     // in the wrong order, etc.), StateTracker will simply return kStateUnknown
762     // when queried using an incorrect key.
763     HashableDimensionKey stateValuesKey = DEFAULT_DIMENSION_KEY;
764     for (auto atomId : mSlicedStateAtoms) {
765         FieldValue value;
766         if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) {
767             // found a primary key for this state, query using the key
768             queryStateValue(atomId, statePrimaryKeys[atomId], &value);
769         } else {
770             // if no MetricStateLinks exist for this state atom,
771             // query using the default dimension key (empty HashableDimensionKey)
772             queryStateValue(atomId, DEFAULT_DIMENSION_KEY, &value);
773         }
774         mapStateValue(atomId, &value);
775         stateValuesKey.addValue(value);
776     }
777 
778     // Handles Stop events.
779     if ((int)matcherIndex == mStopIndex) {
780         if (mUseWhatDimensionAsInternalDimension) {
781             auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
782             if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
783                 whatIt->second->noteStop(dimensionInWhat, eventTimeNs, false);
784             }
785             return;
786         }
787 
788         HashableDimensionKey internalDimensionKey = DEFAULT_DIMENSION_KEY;
789         if (!mInternalDimensions.empty()) {
790             filterValues(mInternalDimensions, values, &internalDimensionKey);
791         }
792 
793         auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
794         if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
795             whatIt->second->noteStop(internalDimensionKey, eventTimeNs, false);
796         }
797         return;
798     }
799 
800     bool condition;
801     ConditionKey conditionKey;
802     if (mConditionSliced) {
803         for (const auto& link : mMetric2ConditionLinks) {
804             getDimensionForCondition(values, link, &conditionKey[link.conditionId]);
805         }
806 
807         auto conditionState =
808             mWizard->query(mConditionTrackerIndex, conditionKey,
809                            !mHasLinksToAllConditionDimensionsInTracker);
810         condition = conditionState == ConditionState::kTrue;
811     } else {
812         // TODO: The unknown condition state is not handled here, we should fix it.
813         condition = mCondition == ConditionState::kTrue;
814     }
815 
816     condition = condition && mIsActive;
817 
818     handleStartEvent(MetricDimensionKey(dimensionInWhat, stateValuesKey), conditionKey, condition,
819                      eventTimeNs, values);
820 }
821 
byteSizeLocked() const822 size_t DurationMetricProducer::byteSizeLocked() const {
823     size_t totalSize = 0;
824     for (const auto& pair : mPastBuckets) {
825         totalSize += pair.second.size() * kBucketSize;
826     }
827     return totalSize;
828 }
829 
830 }  // namespace statsd
831 }  // namespace os
832 }  // namespace android
833