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 DEBUG false  // STOPSHIP if true
17#include "Log.h"
18#include "MetricsManager.h"
19#include "statslog.h"
20
21#include "CountMetricProducer.h"
22#include "condition/CombinationConditionTracker.h"
23#include "condition/SimpleConditionTracker.h"
24#include "guardrail/StatsdStats.h"
25#include "matchers/CombinationLogMatchingTracker.h"
26#include "matchers/SimpleLogMatchingTracker.h"
27#include "metrics_manager_util.h"
28#include "stats_util.h"
29#include "stats_log_util.h"
30
31#include <log/logprint.h>
32#include <private/android_filesystem_config.h>
33#include <utils/SystemClock.h>
34
35using android::util::FIELD_COUNT_REPEATED;
36using android::util::FIELD_TYPE_INT32;
37using android::util::FIELD_TYPE_INT64;
38using android::util::FIELD_TYPE_MESSAGE;
39using android::util::FIELD_TYPE_STRING;
40using android::util::ProtoOutputStream;
41
42using std::make_unique;
43using std::set;
44using std::string;
45using std::unordered_map;
46using std::vector;
47
48namespace android {
49namespace os {
50namespace statsd {
51
52const int FIELD_ID_METRICS = 1;
53const int FIELD_ID_ANNOTATIONS = 7;
54const int FIELD_ID_ANNOTATIONS_INT64 = 1;
55const int FIELD_ID_ANNOTATIONS_INT32 = 2;
56
57MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config,
58                               const int64_t timeBaseNs, const int64_t currentTimeNs,
59                               const sp<UidMap> &uidMap,
60                               const sp<AlarmMonitor>& anomalyAlarmMonitor,
61                               const sp<AlarmMonitor>& periodicAlarmMonitor)
62    : mConfigKey(key), mUidMap(uidMap),
63      mTtlNs(config.has_ttl_in_seconds() ? config.ttl_in_seconds() * NS_PER_SEC : -1),
64      mTtlEndNs(-1),
65      mLastReportTimeNs(currentTimeNs),
66      mLastReportWallClockNs(getWallClockNs()) {
67    // Init the ttl end timestamp.
68    refreshTtl(timeBaseNs);
69
70    mConfigValid =
71            initStatsdConfig(key, config, *uidMap, anomalyAlarmMonitor, periodicAlarmMonitor,
72                             timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchers,
73                             mAllConditionTrackers, mAllMetricProducers, mAllAnomalyTrackers,
74                             mAllPeriodicAlarmTrackers, mConditionToMetricMap, mTrackerToMetricMap,
75                             mTrackerToConditionMap, mNoReportMetricIds);
76
77    mHashStringsInReport = config.hash_strings_in_metric_report();
78
79    if (config.allowed_log_source_size() == 0) {
80        mConfigValid = false;
81        ALOGE("Log source whitelist is empty! This config won't get any data. Suggest adding at "
82                      "least AID_SYSTEM and AID_STATSD to the allowed_log_source field.");
83    } else {
84        for (const auto& source : config.allowed_log_source()) {
85            auto it = UidMap::sAidToUidMapping.find(source);
86            if (it != UidMap::sAidToUidMapping.end()) {
87                mAllowedUid.push_back(it->second);
88            } else {
89                mAllowedPkg.push_back(source);
90            }
91        }
92
93        if (mAllowedUid.size() + mAllowedPkg.size() > StatsdStats::kMaxLogSourceCount) {
94            ALOGE("Too many log sources. This is likely to be an error in the config.");
95            mConfigValid = false;
96        } else {
97            initLogSourceWhiteList();
98        }
99    }
100
101    // Store the sub-configs used.
102    for (const auto& annotation : config.annotation()) {
103        mAnnotations.emplace_back(annotation.field_int64(), annotation.field_int32());
104    }
105
106    // Guardrail. Reject the config if it's too big.
107    if (mAllMetricProducers.size() > StatsdStats::kMaxMetricCountPerConfig ||
108        mAllConditionTrackers.size() > StatsdStats::kMaxConditionCountPerConfig ||
109        mAllAtomMatchers.size() > StatsdStats::kMaxMatcherCountPerConfig) {
110        ALOGE("This config is too big! Reject!");
111        mConfigValid = false;
112    }
113    if (mAllAnomalyTrackers.size() > StatsdStats::kMaxAlertCountPerConfig) {
114        ALOGE("This config has too many alerts! Reject!");
115        mConfigValid = false;
116    }
117    // no matter whether this config is valid, log it in the stats.
118    StatsdStats::getInstance().noteConfigReceived(
119            key, mAllMetricProducers.size(), mAllConditionTrackers.size(), mAllAtomMatchers.size(),
120            mAllAnomalyTrackers.size(), mAnnotations, mConfigValid);
121}
122
123MetricsManager::~MetricsManager() {
124    VLOG("~MetricsManager()");
125}
126
127void MetricsManager::initLogSourceWhiteList() {
128    std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
129    mAllowedLogSources.clear();
130    mAllowedLogSources.insert(mAllowedUid.begin(), mAllowedUid.end());
131
132    for (const auto& pkg : mAllowedPkg) {
133        auto uids = mUidMap->getAppUid(pkg);
134        mAllowedLogSources.insert(uids.begin(), uids.end());
135    }
136    if (DEBUG) {
137        for (const auto& uid : mAllowedLogSources) {
138            VLOG("Allowed uid %d", uid);
139        }
140    }
141}
142
143bool MetricsManager::isConfigValid() const {
144    return mConfigValid;
145}
146
147void MetricsManager::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
148                                      const int64_t version) {
149    // check if we care this package
150    if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) == mAllowedPkg.end()) {
151        return;
152    }
153    // We will re-initialize the whole list because we don't want to keep the multi mapping of
154    // UID<->pkg inside MetricsManager to reduce the memory usage.
155    initLogSourceWhiteList();
156}
157
158void MetricsManager::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk,
159                                      const int uid) {
160    // check if we care this package
161    if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) == mAllowedPkg.end()) {
162        return;
163    }
164    // We will re-initialize the whole list because we don't want to keep the multi mapping of
165    // UID<->pkg inside MetricsManager to reduce the memory usage.
166    initLogSourceWhiteList();
167}
168
169void MetricsManager::onUidMapReceived(const int64_t& eventTimeNs) {
170    if (mAllowedPkg.size() == 0) {
171        return;
172    }
173    initLogSourceWhiteList();
174}
175
176void MetricsManager::dumpStates(FILE* out, bool verbose) {
177    fprintf(out, "ConfigKey %s, allowed source:", mConfigKey.ToString().c_str());
178    {
179        std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
180        for (const auto& source : mAllowedLogSources) {
181            fprintf(out, "%d ", source);
182        }
183    }
184    fprintf(out, "\n");
185    for (const auto& producer : mAllMetricProducers) {
186        producer->dumpStates(out, verbose);
187    }
188}
189
190void MetricsManager::dropData(const int64_t dropTimeNs) {
191    for (const auto& producer : mAllMetricProducers) {
192        producer->dropData(dropTimeNs);
193    }
194}
195
196void MetricsManager::onDumpReport(const int64_t dumpTimeStampNs,
197                                  const bool include_current_partial_bucket,
198                                  std::set<string> *str_set,
199                                  ProtoOutputStream* protoOutput) {
200    VLOG("=========================Metric Reports Start==========================");
201    // one StatsLogReport per MetricProduer
202    for (const auto& producer : mAllMetricProducers) {
203        if (mNoReportMetricIds.find(producer->getMetricId()) == mNoReportMetricIds.end()) {
204            uint64_t token = protoOutput->start(
205                    FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS);
206            if (mHashStringsInReport) {
207                producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, str_set,
208                                       protoOutput);
209            } else {
210                producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, nullptr,
211                                       protoOutput);
212            }
213            protoOutput->end(token);
214        } else {
215            producer->clearPastBuckets(dumpTimeStampNs);
216        }
217    }
218    for (const auto& annotation : mAnnotations) {
219        uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
220                                            FIELD_ID_ANNOTATIONS);
221        protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ANNOTATIONS_INT64,
222                           (long long)annotation.first);
223        protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_ANNOTATIONS_INT32, annotation.second);
224        protoOutput->end(token);
225    }
226
227    mLastReportTimeNs = dumpTimeStampNs;
228    mLastReportWallClockNs = getWallClockNs();
229    VLOG("=========================Metric Reports End==========================");
230}
231
232// Consume the stats log if it's interesting to this metric.
233void MetricsManager::onLogEvent(const LogEvent& event) {
234    if (!mConfigValid) {
235        return;
236    }
237
238    if (event.GetTagId() == android::util::APP_BREADCRUMB_REPORTED) {
239        // Check that app breadcrumb reported fields are valid.
240        // TODO: Find a way to make these checks easier to maintain.
241        status_t err = NO_ERROR;
242
243        // Uid is 3rd from last field and must match the caller's uid,
244        // unless that caller is statsd itself (statsd is allowed to spoof uids).
245        long appHookUid = event.GetLong(event.size()-2, &err);
246        if (err != NO_ERROR ) {
247            VLOG("APP_BREADCRUMB_REPORTED had error when parsing the uid");
248            return;
249        }
250        int32_t loggerUid = event.GetUid();
251        if (loggerUid != appHookUid && loggerUid != AID_STATSD) {
252            VLOG("APP_BREADCRUMB_REPORTED has invalid uid: claimed %ld but caller is %d",
253                 appHookUid, loggerUid);
254            return;
255        }
256
257        // The state must be from 0,3. This part of code must be manually updated.
258        long appHookState = event.GetLong(event.size(), &err);
259        if (err != NO_ERROR ) {
260            VLOG("APP_BREADCRUMB_REPORTED had error when parsing the state field");
261            return;
262        } else if (appHookState < 0 || appHookState > 3) {
263            VLOG("APP_BREADCRUMB_REPORTED does not have valid state %ld", appHookState);
264            return;
265        }
266    } else if (event.GetTagId() == android::util::DAVEY_OCCURRED) {
267        // Daveys can be logged from any app since they are logged in libs/hwui/JankTracker.cpp.
268        // Check that the davey duration is reasonable. Max length check is for privacy.
269        status_t err = NO_ERROR;
270
271        // Uid is the first field provided.
272        long jankUid = event.GetLong(1, &err);
273        if (err != NO_ERROR ) {
274            VLOG("Davey occurred had error when parsing the uid");
275            return;
276        }
277        int32_t loggerUid = event.GetUid();
278        if (loggerUid != jankUid && loggerUid != AID_STATSD) {
279            VLOG("DAVEY_OCCURRED has invalid uid: claimed %ld but caller is %d", jankUid,
280                 loggerUid);
281            return;
282        }
283
284        long duration = event.GetLong(event.size(), &err);
285        if (err != NO_ERROR ) {
286            VLOG("Davey occurred had error when parsing the duration");
287            return;
288        } else if (duration > 100000) {
289            VLOG("Davey duration is unreasonably long: %ld", duration);
290            return;
291        }
292    } else {
293        std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
294        if (mAllowedLogSources.find(event.GetUid()) == mAllowedLogSources.end()) {
295            VLOG("log source %d not on the whitelist", event.GetUid());
296            return;
297        }
298    }
299
300    int tagId = event.GetTagId();
301    int64_t eventTime = event.GetElapsedTimestampNs();
302    if (mTagIds.find(tagId) == mTagIds.end()) {
303        // not interesting...
304        return;
305    }
306
307    vector<MatchingState> matcherCache(mAllAtomMatchers.size(), MatchingState::kNotComputed);
308
309    for (auto& matcher : mAllAtomMatchers) {
310        matcher->onLogEvent(event, mAllAtomMatchers, matcherCache);
311    }
312
313    // A bitmap to see which ConditionTracker needs to be re-evaluated.
314    vector<bool> conditionToBeEvaluated(mAllConditionTrackers.size(), false);
315
316    for (const auto& pair : mTrackerToConditionMap) {
317        if (matcherCache[pair.first] == MatchingState::kMatched) {
318            const auto& conditionList = pair.second;
319            for (const int conditionIndex : conditionList) {
320                conditionToBeEvaluated[conditionIndex] = true;
321            }
322        }
323    }
324
325    vector<ConditionState> conditionCache(mAllConditionTrackers.size(),
326                                          ConditionState::kNotEvaluated);
327    // A bitmap to track if a condition has changed value.
328    vector<bool> changedCache(mAllConditionTrackers.size(), false);
329    for (size_t i = 0; i < mAllConditionTrackers.size(); i++) {
330        if (conditionToBeEvaluated[i] == false) {
331            continue;
332        }
333        sp<ConditionTracker>& condition = mAllConditionTrackers[i];
334        condition->evaluateCondition(event, matcherCache, mAllConditionTrackers, conditionCache,
335                                     changedCache);
336    }
337
338    for (size_t i = 0; i < mAllConditionTrackers.size(); i++) {
339        if (changedCache[i] == false) {
340            continue;
341        }
342        auto pair = mConditionToMetricMap.find(i);
343        if (pair != mConditionToMetricMap.end()) {
344            auto& metricList = pair->second;
345            for (auto metricIndex : metricList) {
346                // metric cares about non sliced condition, and it's changed.
347                // Push the new condition to it directly.
348                if (!mAllMetricProducers[metricIndex]->isConditionSliced()) {
349                    mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i],
350                                                                         eventTime);
351                    // metric cares about sliced conditions, and it may have changed. Send
352                    // notification, and the metric can query the sliced conditions that are
353                    // interesting to it.
354                } else {
355                    mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(conditionCache[i],
356                                                                                 eventTime);
357                }
358            }
359        }
360    }
361
362    // For matched AtomMatchers, tell relevant metrics that a matched event has come.
363    for (size_t i = 0; i < mAllAtomMatchers.size(); i++) {
364        if (matcherCache[i] == MatchingState::kMatched) {
365            StatsdStats::getInstance().noteMatcherMatched(mConfigKey,
366                                                          mAllAtomMatchers[i]->getId());
367            auto pair = mTrackerToMetricMap.find(i);
368            if (pair != mTrackerToMetricMap.end()) {
369                auto& metricList = pair->second;
370                for (const int metricIndex : metricList) {
371                    // pushed metrics are never scheduled pulls
372                    mAllMetricProducers[metricIndex]->onMatchedLogEvent(i, event);
373                }
374            }
375        }
376    }
377}
378
379void MetricsManager::onAnomalyAlarmFired(
380        const int64_t& timestampNs,
381        unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) {
382    for (const auto& itr : mAllAnomalyTrackers) {
383        itr->informAlarmsFired(timestampNs, alarmSet);
384    }
385}
386
387void MetricsManager::onPeriodicAlarmFired(
388        const int64_t& timestampNs,
389        unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) {
390    for (const auto& itr : mAllPeriodicAlarmTrackers) {
391        itr->informAlarmsFired(timestampNs, alarmSet);
392    }
393}
394
395// Returns the total byte size of all metrics managed by a single config source.
396size_t MetricsManager::byteSize() {
397    size_t totalSize = 0;
398    for (auto metricProducer : mAllMetricProducers) {
399        totalSize += metricProducer->byteSize();
400    }
401    return totalSize;
402}
403
404}  // namespace statsd
405}  // namespace os
406}  // namespace android
407