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  // STOPSHIP if true
18 #include "Log.h"
19 
20 #include "MetricProducer.h"
21 
22 #include "../guardrail/StatsdStats.h"
23 #include "metrics/parsing_utils/metrics_manager_util.h"
24 #include "state/StateTracker.h"
25 
26 using android::util::FIELD_COUNT_REPEATED;
27 using android::util::FIELD_TYPE_ENUM;
28 using android::util::FIELD_TYPE_INT32;
29 using android::util::FIELD_TYPE_INT64;
30 using android::util::FIELD_TYPE_MESSAGE;
31 using android::util::ProtoOutputStream;
32 
33 namespace android {
34 namespace os {
35 namespace statsd {
36 
37 // for ActiveMetric
38 const int FIELD_ID_ACTIVE_METRIC_ID = 1;
39 const int FIELD_ID_ACTIVE_METRIC_ACTIVATION = 2;
40 
41 // for ActiveEventActivation
42 const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX = 1;
43 const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS = 2;
44 const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE = 3;
45 
MetricProducer(int64_t metricId,const ConfigKey & key,const int64_t timeBaseNs,const int conditionIndex,const vector<ConditionState> & initialConditionCache,const sp<ConditionWizard> & wizard,const uint64_t protoHash,const std::unordered_map<int,std::shared_ptr<Activation>> & eventActivationMap,const std::unordered_map<int,std::vector<std::shared_ptr<Activation>>> & eventDeactivationMap,const vector<int> & slicedStateAtoms,const unordered_map<int,unordered_map<int,int64_t>> & stateGroupMap,const optional<bool> splitBucketForAppUpgrade,const wp<ConfigMetadataProvider> configMetadataProvider)46 MetricProducer::MetricProducer(
47         int64_t metricId, const ConfigKey& key, const int64_t timeBaseNs, const int conditionIndex,
48         const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
49         const uint64_t protoHash,
50         const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
51         const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
52                 eventDeactivationMap,
53         const vector<int>& slicedStateAtoms,
54         const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap,
55         const optional<bool> splitBucketForAppUpgrade,
56         const wp<ConfigMetadataProvider> configMetadataProvider)
57     : mMetricId(metricId),
58       mProtoHash(protoHash),
59       mConfigKey(key),
60       mValid(true),
61       mTimeBaseNs(timeBaseNs),
62       mCurrentBucketStartTimeNs(timeBaseNs),
63       mCurrentBucketNum(0),
64       mCondition(initialCondition(conditionIndex, initialConditionCache)),
65       // For metrics with pull events, condition timer will be set later within the constructor
66       mConditionTimer(false, timeBaseNs),
67       mConditionTrackerIndex(conditionIndex),
68       mConditionSliced(false),
69       mWizard(wizard),
70       mContainANYPositionInDimensionsInWhat(false),
71       mShouldUseNestedDimensions(false),
72       mHasLinksToAllConditionDimensionsInTracker(false),
73       mEventActivationMap(eventActivationMap),
74       mEventDeactivationMap(eventDeactivationMap),
75       mIsActive(mEventActivationMap.empty()),
76       mSlicedStateAtoms(slicedStateAtoms),
77       mStateGroupMap(stateGroupMap),
78       mSplitBucketForAppUpgrade(splitBucketForAppUpgrade),
79       mHasHitGuardrail(false),
80       mSampledWhatFields({}),
81       mShardCount(0),
82       mConfigMetadataProvider(configMetadataProvider) {
83 }
84 
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)85 optional<InvalidConfigReason> MetricProducer::onConfigUpdatedLocked(
86         const StatsdConfig& config, const int configIndex, const int metricIndex,
87         const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
88         const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
89         const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
90         const sp<EventMatcherWizard>& matcherWizard,
91         const vector<sp<ConditionTracker>>& allConditionTrackers,
92         const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
93         const unordered_map<int64_t, int>& metricToActivationMap,
94         unordered_map<int, vector<int>>& trackerToMetricMap,
95         unordered_map<int, vector<int>>& conditionToMetricMap,
96         unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
97         unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
98         vector<int>& metricsWithActivation) {
99     sp<ConditionWizard> tmpWizard = mWizard;
100     mWizard = wizard;
101 
102     unordered_map<int, shared_ptr<Activation>> newEventActivationMap;
103     unordered_map<int, vector<shared_ptr<Activation>>> newEventDeactivationMap;
104     optional<InvalidConfigReason> invalidConfigReason = handleMetricActivationOnConfigUpdate(
105             config, mMetricId, metricIndex, metricToActivationMap, oldAtomMatchingTrackerMap,
106             newAtomMatchingTrackerMap, mEventActivationMap, activationAtomTrackerToMetricMap,
107             deactivationAtomTrackerToMetricMap, metricsWithActivation, newEventActivationMap,
108             newEventDeactivationMap);
109     if (invalidConfigReason.has_value()) {
110         return invalidConfigReason;
111     }
112     mEventActivationMap = newEventActivationMap;
113     mEventDeactivationMap = newEventDeactivationMap;
114     mAnomalyTrackers.clear();
115     return nullopt;
116 }
117 
onMatchedLogEventLocked(const size_t matcherIndex,const LogEvent & event)118 void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
119     if (!mIsActive) {
120         return;
121     }
122     int64_t eventTimeNs = event.GetElapsedTimestampNs();
123     // this is old event, maybe statsd restarted?
124     if (eventTimeNs < mTimeBaseNs) {
125         return;
126     }
127 
128     if (!passesSampleCheckLocked(event.getValues())) {
129         return;
130     }
131 
132     bool condition;
133     ConditionKey conditionKey;
134     if (mConditionSliced) {
135         for (const auto& link : mMetric2ConditionLinks) {
136             getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
137         }
138         auto conditionState =
139             mWizard->query(mConditionTrackerIndex, conditionKey,
140                            !mHasLinksToAllConditionDimensionsInTracker);
141         condition = (conditionState == ConditionState::kTrue);
142     } else {
143         // TODO: The unknown condition state is not handled here, we should fix it.
144         condition = mCondition == ConditionState::kTrue;
145     }
146 
147     // Stores atom id to primary key pairs for each state atom that the metric is
148     // sliced by.
149     std::map<int32_t, HashableDimensionKey> statePrimaryKeys;
150 
151     // For states with primary fields, use MetricStateLinks to get the primary
152     // field values from the log event. These values will form a primary key
153     // that will be used to query StateTracker for the correct state value.
154     for (const auto& stateLink : mMetric2StateLinks) {
155         getDimensionForState(event.getValues(), stateLink,
156                              &statePrimaryKeys[stateLink.stateAtomId]);
157     }
158 
159     // For each sliced state, query StateTracker for the state value using
160     // either the primary key from the previous step or the DEFAULT_DIMENSION_KEY.
161     //
162     // Expected functionality: for any case where the MetricStateLinks are
163     // initialized incorrectly (ex. # of state links != # of primary fields, no
164     // links are provided for a state with primary fields, links are provided
165     // in the wrong order, etc.), StateTracker will simply return kStateUnknown
166     // when queried using an incorrect key.
167     HashableDimensionKey stateValuesKey;
168     for (auto atomId : mSlicedStateAtoms) {
169         FieldValue value;
170         if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) {
171             // found a primary key for this state, query using the key
172             queryStateValue(atomId, statePrimaryKeys[atomId], &value);
173         } else {
174             // if no MetricStateLinks exist for this state atom,
175             // query using the default dimension key (empty HashableDimensionKey)
176             queryStateValue(atomId, DEFAULT_DIMENSION_KEY, &value);
177         }
178         mapStateValue(atomId, &value);
179         stateValuesKey.addValue(value);
180     }
181 
182     HashableDimensionKey dimensionInWhat;
183     filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
184     MetricDimensionKey metricKey(dimensionInWhat, stateValuesKey);
185     onMatchedLogEventInternalLocked(matcherIndex, metricKey, conditionKey, condition, event,
186                                     statePrimaryKeys);
187 }
188 
189 /**
190  * @brief Updates data corruption state overriding lower severity state with a higher
191  *        Inherited classes are responsible for proper severity determination according
192  *        to loss parameters (see @determineCorruptionSeverity)
193  */
onMatchedLogEventLostLocked(int32_t atomId,DataCorruptedReason reason,LostAtomType atomType)194 void MetricProducer::onMatchedLogEventLostLocked(int32_t atomId, DataCorruptedReason reason,
195                                                  LostAtomType atomType) {
196     const DataCorruptionSeverity newSeverity =
197             determineCorruptionSeverity(atomId, reason, atomType);
198     switch (reason) {
199         case DATA_CORRUPTED_SOCKET_LOSS:
200             mDataCorruptedDueToSocketLoss = std::max(mDataCorruptedDueToSocketLoss, newSeverity);
201             break;
202         case DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW:
203             mDataCorruptedDueToQueueOverflow =
204                     std::max(mDataCorruptedDueToQueueOverflow, newSeverity);
205             break;
206         default:
207             ALOGW("Unsupported data corruption reason reported %d", reason);
208             break;
209     }
210 }
211 
resetDataCorruptionFlagsLocked()212 void MetricProducer::resetDataCorruptionFlagsLocked() {
213     if (mDataCorruptedDueToSocketLoss != DataCorruptionSeverity::kUnrecoverable) {
214         mDataCorruptedDueToSocketLoss = DataCorruptionSeverity::kNone;
215     }
216     if (mDataCorruptedDueToQueueOverflow != DataCorruptionSeverity::kUnrecoverable) {
217         mDataCorruptedDueToQueueOverflow = DataCorruptionSeverity::kNone;
218     }
219 }
220 
evaluateActiveStateLocked(int64_t elapsedTimestampNs)221 bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) {
222     bool isActive = mEventActivationMap.empty();
223     for (auto& it : mEventActivationMap) {
224         if (it.second->state == ActivationState::kActive &&
225             elapsedTimestampNs > it.second->ttl_ns + it.second->start_ns) {
226             it.second->state = ActivationState::kNotActive;
227         }
228         if (it.second->state == ActivationState::kActive) {
229             isActive = true;
230         }
231     }
232     return isActive;
233 }
234 
flushIfExpire(int64_t elapsedTimestampNs)235 void MetricProducer::flushIfExpire(int64_t elapsedTimestampNs) {
236     std::lock_guard<std::mutex> lock(mMutex);
237     if (!mIsActive) {
238         return;
239     }
240     const bool isActive = evaluateActiveStateLocked(elapsedTimestampNs);
241     if (!isActive) {  // Metric went from active to not active.
242         onActiveStateChangedLocked(elapsedTimestampNs, false);
243 
244         // Set mIsActive to false after onActiveStateChangedLocked to ensure any pulls that occur
245         // through onActiveStateChangedLocked are processed.
246         mIsActive = false;
247     }
248 }
249 
activateLocked(int activationTrackerIndex,int64_t elapsedTimestampNs)250 void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs) {
251     auto it = mEventActivationMap.find(activationTrackerIndex);
252     if (it == mEventActivationMap.end()) {
253         return;
254     }
255     auto& activation = it->second;
256     if (ACTIVATE_ON_BOOT == activation->activationType) {
257         if (ActivationState::kNotActive == activation->state) {
258             activation->state = ActivationState::kActiveOnBoot;
259         }
260         // If the Activation is already active or set to kActiveOnBoot, do nothing.
261         return;
262     }
263     activation->start_ns = elapsedTimestampNs;
264     activation->state = ActivationState::kActive;
265     if (!mIsActive) {  // Metric was previously inactive and now is active.
266         // Set mIsActive to true before onActiveStateChangedLocked to ensure any pulls that occur
267         // through onActiveStateChangedLocked are processed.
268         mIsActive = true;
269 
270         onActiveStateChangedLocked(elapsedTimestampNs, true);
271     }
272 }
273 
cancelEventActivationLocked(int deactivationTrackerIndex)274 void MetricProducer::cancelEventActivationLocked(int deactivationTrackerIndex) {
275     auto it = mEventDeactivationMap.find(deactivationTrackerIndex);
276     if (it == mEventDeactivationMap.end()) {
277         return;
278     }
279     for (auto& activationToCancelIt : it->second) {
280         activationToCancelIt->state = ActivationState::kNotActive;
281     }
282 }
283 
loadActiveMetricLocked(const ActiveMetric & activeMetric,int64_t currentTimeNs)284 void MetricProducer::loadActiveMetricLocked(const ActiveMetric& activeMetric,
285                                             int64_t currentTimeNs) {
286     if (mEventActivationMap.size() == 0) {
287         return;
288     }
289     for (int i = 0; i < activeMetric.activation_size(); i++) {
290         const auto& activeEventActivation = activeMetric.activation(i);
291         auto it = mEventActivationMap.find(activeEventActivation.atom_matcher_index());
292         if (it == mEventActivationMap.end()) {
293             ALOGE("Saved event activation not found");
294             continue;
295         }
296         auto& activation = it->second;
297         // If the event activation does not have a state, assume it is active.
298         if (!activeEventActivation.has_state() ||
299                 activeEventActivation.state() == ActiveEventActivation::ACTIVE) {
300             // We don't want to change the ttl for future activations, so we set the start_ns
301             // such that start_ns + ttl_ns == currentTimeNs + remaining_ttl_nanos
302             activation->start_ns =
303                 currentTimeNs + activeEventActivation.remaining_ttl_nanos() - activation->ttl_ns;
304             activation->state = ActivationState::kActive;
305             mIsActive = true;
306         } else if (activeEventActivation.state() == ActiveEventActivation::ACTIVATE_ON_BOOT) {
307             activation->state = ActivationState::kActiveOnBoot;
308         }
309     }
310 }
311 
writeActiveMetricToProtoOutputStream(int64_t currentTimeNs,const DumpReportReason reason,ProtoOutputStream * proto)312 void MetricProducer::writeActiveMetricToProtoOutputStream(
313         int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
314     proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_METRIC_ID, (long long)mMetricId);
315     for (auto& it : mEventActivationMap) {
316         const int atom_matcher_index = it.first;
317         const std::shared_ptr<Activation>& activation = it.second;
318 
319         if (ActivationState::kNotActive == activation->state ||
320                 (ActivationState::kActive == activation->state &&
321                  activation->start_ns + activation->ttl_ns < currentTimeNs)) {
322             continue;
323         }
324 
325         const uint64_t activationToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
326                 FIELD_ID_ACTIVE_METRIC_ACTIVATION);
327         proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX,
328                 atom_matcher_index);
329         if (ActivationState::kActive == activation->state) {
330             const int64_t remainingTtlNs =
331                     activation->start_ns + activation->ttl_ns - currentTimeNs;
332             proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
333                     (long long)remainingTtlNs);
334             proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
335                     ActiveEventActivation::ACTIVE);
336 
337         } else if (ActivationState::kActiveOnBoot == activation->state) {
338             if (reason == DEVICE_SHUTDOWN || reason == TERMINATION_SIGNAL_RECEIVED) {
339                 proto->write(
340                         FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
341                         (long long)activation->ttl_ns);
342                 proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
343                                     ActiveEventActivation::ACTIVE);
344             } else if (reason == STATSCOMPANION_DIED) {
345                 // We are saving because of system server death, not due to a device shutdown.
346                 // Next time we load, we do not want to activate metrics that activate on boot.
347                 proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
348                                                     ActiveEventActivation::ACTIVATE_ON_BOOT);
349             }
350         }
351         proto->end(activationToken);
352     }
353 }
354 
queryStateValue(int32_t atomId,const HashableDimensionKey & queryKey,FieldValue * value)355 void MetricProducer::queryStateValue(int32_t atomId, const HashableDimensionKey& queryKey,
356                                      FieldValue* value) {
357     if (!StateManager::getInstance().getStateValue(atomId, queryKey, value)) {
358         value->mValue = Value(StateTracker::kStateUnknown);
359         value->mField.setTag(atomId);
360     }
361 }
362 
mapStateValue(int32_t atomId,FieldValue * value)363 void MetricProducer::mapStateValue(int32_t atomId, FieldValue* value) {
364     // check if there is a state map for this atom
365     auto atomIt = mStateGroupMap.find(atomId);
366     if (atomIt == mStateGroupMap.end()) {
367         return;
368     }
369     auto valueIt = atomIt->second.find(value->mValue.int_value);
370     if (valueIt == atomIt->second.end()) {
371         // state map exists, but value was not put in a state group
372         // so set mValue to kStateUnknown
373         // TODO(tsaichristine): handle incomplete state maps
374         value->mValue.setInt(StateTracker::kStateUnknown);
375     } else {
376         // set mValue to group_id
377         value->mValue.setLong(valueIt->second);
378     }
379 }
380 
getUnknownStateKey()381 HashableDimensionKey MetricProducer::getUnknownStateKey() {
382     HashableDimensionKey stateKey;
383     for (auto atom : mSlicedStateAtoms) {
384         FieldValue fieldValue;
385         fieldValue.mField.setTag(atom);
386         fieldValue.mValue.setInt(StateTracker::kStateUnknown);
387         stateKey.addValue(fieldValue);
388     }
389     return stateKey;
390 }
391 
buildDropEvent(const int64_t dropTimeNs,const BucketDropReason reason) const392 DropEvent MetricProducer::buildDropEvent(const int64_t dropTimeNs,
393                                          const BucketDropReason reason) const {
394     DropEvent event;
395     event.reason = reason;
396     event.dropTimeNs = dropTimeNs;
397     return event;
398 }
399 
maxDropEventsReached() const400 bool MetricProducer::maxDropEventsReached() const {
401     return mCurrentSkippedBucket.dropEvents.size() >= StatsdStats::kMaxLoggedBucketDropEvents;
402 }
403 
passesSampleCheckLocked(const vector<FieldValue> & values) const404 bool MetricProducer::passesSampleCheckLocked(const vector<FieldValue>& values) const {
405     // Only perform sampling if shard count is correct and there is a sampled what field.
406     if (mShardCount <= 1 || mSampledWhatFields.size() == 0) {
407         return true;
408     }
409     // If filtering fails, don't perform sampling. Event could be a gauge trigger event or stop all
410     // event.
411     FieldValue sampleFieldValue;
412     if (!filterValues(mSampledWhatFields[0], values, &sampleFieldValue)) {
413         return true;
414     }
415     return shouldKeepSample(sampleFieldValue, ShardOffsetProvider::getInstance().getShardOffset(),
416                             mShardCount);
417 }
418 
getConfigMetadataProvider() const419 sp<ConfigMetadataProvider> MetricProducer::getConfigMetadataProvider() const {
420     sp<ConfigMetadataProvider> provider = mConfigMetadataProvider.promote();
421     if (provider == nullptr) {
422         ALOGE("Could not promote ConfigMetadataProvider");
423         StatsdStats::getInstance().noteConfigMetadataProviderPromotionFailed(mConfigKey);
424     }
425     return provider;
426 }
427 
computeBucketSizeLocked(const bool isFullBucket,const MetricDimensionKey & dimKey,const bool isFirstBucket) const428 size_t MetricProducer::computeBucketSizeLocked(const bool isFullBucket,
429                                                const MetricDimensionKey& dimKey,
430                                                const bool isFirstBucket) const {
431     size_t bucketSize = 0;
432 
433     // Bucket timestamps or bucket number
434     bucketSize += isFullBucket ? sizeof(int32_t) : 2 * sizeof(int64_t);
435 
436     // Each dimension / state key can have multiple buckets. Add the size only for the first bucket.
437     if (isFirstBucket) {
438         bucketSize += dimKey.getSize(mShouldUseNestedDimensions);
439     }
440 
441     return bucketSize;
442 }
443 
computeOverheadSizeLocked(const bool hasPastBuckets,const bool dimensionGuardrailHit) const444 size_t MetricProducer::computeOverheadSizeLocked(const bool hasPastBuckets,
445                                                  const bool dimensionGuardrailHit) const {
446     size_t overheadSize = 0;
447 
448     // MetricId + isActive
449     overheadSize += sizeof(int64_t) + sizeof(bool);
450 
451     if (hasPastBuckets) {
452         if (dimensionGuardrailHit) {
453             overheadSize += sizeof(int32_t);
454         }
455 
456         // estimated_memory_bytes
457         overheadSize += sizeof(int32_t);
458         // mTimeBase and mBucketSizeNs
459         overheadSize += 2 * sizeof(int64_t);
460 
461         if (!mShouldUseNestedDimensions) {
462             // Assume dimensions data adds an additional atomTag + # of dimension fields
463             overheadSize += sizeof(int32_t);
464             overheadSize += sizeof(int32_t) * mDimensionsInWhat.size();
465         }
466     }
467 
468     const int32_t dataCorruptedReasonsCount =
469             (mDataCorruptedDueToQueueOverflow != DataCorruptionSeverity::kNone) +
470             (mDataCorruptedDueToSocketLoss != DataCorruptionSeverity::kNone);
471     if (dataCorruptedReasonsCount > 0) {
472         // adding extra int32 to account for the array length
473         overheadSize += (dataCorruptedReasonsCount + 1) * sizeof(int32_t);
474     }
475 
476     return overheadSize;
477 }
478 
computeSkippedBucketSizeLocked(const SkippedBucket & skippedBucket) const479 size_t MetricProducer::computeSkippedBucketSizeLocked(const SkippedBucket& skippedBucket) const {
480     size_t skippedBucketSize = 0;
481 
482     // Bucket Start, Bucket End
483     skippedBucketSize += 2 * sizeof(int64_t);
484 
485     // DropType, Drop Time
486     skippedBucketSize += (sizeof(int32_t) + sizeof(int64_t)) * skippedBucket.dropEvents.size();
487 
488     return skippedBucketSize;
489 }
490 
491 }  // namespace statsd
492 }  // namespace os
493 }  // namespace android
494