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#pragma once
18
19#include <gtest/gtest_prod.h>
20#include <utils/threads.h>
21#include <list>
22#include "../anomaly/AnomalyTracker.h"
23#include "../condition/ConditionTracker.h"
24#include "../external/PullDataReceiver.h"
25#include "../external/StatsPullerManager.h"
26#include "MetricProducer.h"
27#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
28
29namespace android {
30namespace os {
31namespace statsd {
32
33struct ValueBucket {
34    int64_t mBucketStartNs;
35    int64_t mBucketEndNs;
36    int64_t mValue;
37};
38
39class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
40public:
41    ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
42                        const int conditionIndex, const sp<ConditionWizard>& wizard,
43                        const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs);
44
45    virtual ~ValueMetricProducer();
46
47    void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) override;
48
49    // ValueMetric needs special logic if it's a pulled atom.
50    void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
51                          const int64_t version) override {
52        std::lock_guard<std::mutex> lock(mMutex);
53
54        if (mPullTagId != -1 && (mCondition == true || mConditionTrackerIndex < 0) ) {
55            vector<shared_ptr<LogEvent>> allData;
56            mStatsPullerManager->Pull(mPullTagId, eventTimeNs, &allData);
57            if (allData.size() == 0) {
58                // This shouldn't happen since this valuemetric is not useful now.
59            }
60
61            // Pretend the pulled data occurs right before the app upgrade event.
62            mCondition = false;
63            for (const auto& data : allData) {
64                data->setElapsedTimestampNs(eventTimeNs - 1);
65                onMatchedLogEventLocked(0, *data);
66            }
67
68            flushCurrentBucketLocked(eventTimeNs);
69            mCurrentBucketStartTimeNs = eventTimeNs;
70
71            mCondition = true;
72            for (const auto& data : allData) {
73                data->setElapsedTimestampNs(eventTimeNs);
74                onMatchedLogEventLocked(0, *data);
75            }
76        } else {
77            // For pushed value metric or pulled metric where condition is not true,
78            // we simply flush and reset the current bucket start.
79            flushCurrentBucketLocked(eventTimeNs);
80            mCurrentBucketStartTimeNs = eventTimeNs;
81        }
82    };
83
84protected:
85    void onMatchedLogEventInternalLocked(
86            const size_t matcherIndex, const MetricDimensionKey& eventKey,
87            const ConditionKey& conditionKey, bool condition,
88            const LogEvent& event) override;
89
90private:
91    void onDumpReportLocked(const int64_t dumpTimeNs,
92                            const bool include_current_partial_bucket,
93                            std::set<string> *str_set,
94                            android::util::ProtoOutputStream* protoOutput) override;
95    void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
96
97    // Internal interface to handle condition change.
98    void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
99
100    // Internal interface to handle sliced condition change.
101    void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
102
103    // Internal function to calculate the current used bytes.
104    size_t byteSizeLocked() const override;
105
106    void dumpStatesLocked(FILE* out, bool verbose) const override;
107
108    // Util function to flush the old packet.
109    void flushIfNeededLocked(const int64_t& eventTime) override;
110
111    void flushCurrentBucketLocked(const int64_t& eventTimeNs) override;
112
113    void dropDataLocked(const int64_t dropTimeNs) override;
114
115    const FieldMatcher mValueField;
116
117    std::shared_ptr<StatsPullerManager> mStatsPullerManager;
118
119    // for testing
120    ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
121                        const int conditionIndex, const sp<ConditionWizard>& wizard,
122                        const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs,
123                        std::shared_ptr<StatsPullerManager> statsPullerManager);
124
125    // tagId for pulled data. -1 if this is not pulled
126    const int mPullTagId;
127
128    int mField;
129
130    // internal state of a bucket.
131    typedef struct {
132        // Pulled data always come in pair of <start, end>. This holds the value
133        // for start. The diff (end - start) is added to sum.
134        int64_t start;
135        // Whether the start data point is updated
136        bool startUpdated;
137        // If end data point comes before the start, record this pair as tainted
138        // and the value is not added to the running sum.
139        int tainted;
140        // Running sum of known pairs in this bucket
141        int64_t sum;
142        // If this dimension has any non-tainted value. If not, don't report the
143        // dimension.
144        bool hasValue;
145    } Interval;
146
147    std::unordered_map<MetricDimensionKey, Interval> mCurrentSlicedBucket;
148
149    std::unordered_map<MetricDimensionKey, int64_t> mCurrentFullBucket;
150
151    // Save the past buckets and we can clear when the StatsLogReport is dumped.
152    // TODO: Add a lock to mPastBuckets.
153    std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>> mPastBuckets;
154
155    // Pairs of (elapsed start, elapsed end) denoting buckets that were skipped.
156    std::list<std::pair<int64_t, int64_t>> mSkippedBuckets;
157
158    const int64_t mMinBucketSizeNs;
159
160    // Util function to check whether the specified dimension hits the guardrail.
161    bool hitGuardRailLocked(const MetricDimensionKey& newKey);
162
163    static const size_t kBucketSize = sizeof(ValueBucket{});
164
165    const size_t mDimensionSoftLimit;
166
167    const size_t mDimensionHardLimit;
168
169    const bool mUseAbsoluteValueOnReset;
170
171    FRIEND_TEST(ValueMetricProducerTest, TestNonDimensionalEvents);
172    FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
173    FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset);
174    FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
175    FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade);
176    FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade);
177    FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse);
178    FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition);
179    FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithCondition);
180    FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection);
181    FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition);
182    FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition);
183    FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2);
184    FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition3);
185};
186
187}  // namespace statsd
188}  // namespace os
189}  // namespace android
190