StatsLogProcessor.cpp revision 1c58f04cd34291584b7bf2b45a54427e0ef650c8
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 DEBUG false // STOPSHIP if true
18#include "Log.h"
19#include "statslog.h"
20
21#include <android-base/file.h>
22#include <dirent.h>
23#include "StatsLogProcessor.h"
24#include "stats_log_util.h"
25#include "android-base/stringprintf.h"
26#include "guardrail/StatsdStats.h"
27#include "metrics/CountMetricProducer.h"
28#include "external/StatsPullerManager.h"
29#include "stats_util.h"
30#include "storage/StorageManager.h"
31
32#include <log/log_event_list.h>
33#include <utils/Errors.h>
34#include <utils/SystemClock.h>
35
36using namespace android;
37using android::base::StringPrintf;
38using android::util::FIELD_COUNT_REPEATED;
39using android::util::FIELD_TYPE_BOOL;
40using android::util::FIELD_TYPE_FLOAT;
41using android::util::FIELD_TYPE_INT32;
42using android::util::FIELD_TYPE_INT64;
43using android::util::FIELD_TYPE_MESSAGE;
44using android::util::FIELD_TYPE_STRING;
45using android::util::ProtoOutputStream;
46using std::make_unique;
47using std::unique_ptr;
48using std::vector;
49
50namespace android {
51namespace os {
52namespace statsd {
53
54// for ConfigMetricsReportList
55const int FIELD_ID_CONFIG_KEY = 1;
56const int FIELD_ID_REPORTS = 2;
57// for ConfigKey
58const int FIELD_ID_UID = 1;
59const int FIELD_ID_ID = 2;
60// for ConfigMetricsReport
61// const int FIELD_ID_METRICS = 1; // written in MetricsManager.cpp
62const int FIELD_ID_UID_MAP = 2;
63const int FIELD_ID_LAST_REPORT_ELAPSED_NANOS = 3;
64const int FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS = 4;
65const int FIELD_ID_LAST_REPORT_WALL_CLOCK_NANOS = 5;
66const int FIELD_ID_CURRENT_REPORT_WALL_CLOCK_NANOS = 6;
67const int FIELD_ID_DUMP_REPORT_REASON = 8;
68const int FIELD_ID_STRINGS = 9;
69
70#define NS_PER_HOUR 3600 * NS_PER_SEC
71
72#define STATS_DATA_DIR "/data/misc/stats-data"
73
74StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
75                                     const sp<AlarmMonitor>& anomalyAlarmMonitor,
76                                     const sp<AlarmMonitor>& periodicAlarmMonitor,
77                                     const int64_t timeBaseNs,
78                                     const std::function<bool(const ConfigKey&)>& sendBroadcast)
79    : mUidMap(uidMap),
80      mAnomalyAlarmMonitor(anomalyAlarmMonitor),
81      mPeriodicAlarmMonitor(periodicAlarmMonitor),
82      mSendBroadcast(sendBroadcast),
83      mTimeBaseNs(timeBaseNs),
84      mLargestTimestampSeen(0),
85      mLastTimestampSeen(0) {
86    mStatsPullerManager.ForceClearPullerCache();
87}
88
89StatsLogProcessor::~StatsLogProcessor() {
90}
91
92void StatsLogProcessor::onAnomalyAlarmFired(
93        const int64_t& timestampNs,
94        unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet) {
95    std::lock_guard<std::mutex> lock(mMetricsMutex);
96    for (const auto& itr : mMetricsManagers) {
97        itr.second->onAnomalyAlarmFired(timestampNs, alarmSet);
98    }
99}
100void StatsLogProcessor::onPeriodicAlarmFired(
101        const int64_t& timestampNs,
102        unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet) {
103
104    std::lock_guard<std::mutex> lock(mMetricsMutex);
105    for (const auto& itr : mMetricsManagers) {
106        itr.second->onPeriodicAlarmFired(timestampNs, alarmSet);
107    }
108}
109
110void updateUid(Value* value, int hostUid) {
111    int uid = value->int_value;
112    if (uid != hostUid) {
113        value->setInt(hostUid);
114    }
115}
116
117void StatsLogProcessor::mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const {
118    if (android::util::AtomsInfo::kAtomsWithAttributionChain.find(event->GetTagId()) !=
119        android::util::AtomsInfo::kAtomsWithAttributionChain.end()) {
120        for (auto& value : *(event->getMutableValues())) {
121            if (value.mField.getPosAtDepth(0) > kAttributionField) {
122                break;
123            }
124            if (isAttributionUidField(value)) {
125                const int hostUid = mUidMap->getHostUidOrSelf(value.mValue.int_value);
126                updateUid(&value.mValue, hostUid);
127            }
128        }
129    } else {
130        auto it = android::util::AtomsInfo::kAtomsWithUidField.find(event->GetTagId());
131        if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
132            int uidField = it->second;  // uidField is the field number in proto,
133                                        // starting from 1
134            if (uidField > 0 && (int)event->getValues().size() >= uidField &&
135                (event->getValues())[uidField - 1].mValue.getType() == INT) {
136                Value& value = (*event->getMutableValues())[uidField - 1].mValue;
137                const int hostUid = mUidMap->getHostUidOrSelf(value.int_value);
138                updateUid(&value, hostUid);
139            } else {
140                ALOGE("Malformed log, uid not found. %s", event->ToString().c_str());
141            }
142        }
143    }
144}
145
146void StatsLogProcessor::onIsolatedUidChangedEventLocked(const LogEvent& event) {
147    status_t err = NO_ERROR, err2 = NO_ERROR, err3 = NO_ERROR;
148    bool is_create = event.GetBool(3, &err);
149    auto parent_uid = int(event.GetLong(1, &err2));
150    auto isolated_uid = int(event.GetLong(2, &err3));
151    if (err == NO_ERROR && err2 == NO_ERROR && err3 == NO_ERROR) {
152        if (is_create) {
153            mUidMap->assignIsolatedUid(isolated_uid, parent_uid);
154        } else {
155            mUidMap->removeIsolatedUid(isolated_uid, parent_uid);
156        }
157    } else {
158        ALOGE("Failed to parse uid in the isolated uid change event.");
159    }
160}
161
162void StatsLogProcessor::OnLogEvent(LogEvent* event) {
163    OnLogEvent(event, false);
164}
165
166void StatsLogProcessor::resetConfigs() {
167    std::lock_guard<std::mutex> lock(mMetricsMutex);
168    resetConfigsLocked(getElapsedRealtimeNs());
169}
170
171void StatsLogProcessor::resetConfigsLocked(const int64_t timestampNs) {
172    std::vector<ConfigKey> configKeys;
173    for (auto it = mMetricsManagers.begin(); it != mMetricsManagers.end(); it++) {
174        configKeys.push_back(it->first);
175    }
176    resetConfigsLocked(timestampNs, configKeys);
177}
178
179void StatsLogProcessor::OnLogEvent(LogEvent* event, bool reconnected) {
180    std::lock_guard<std::mutex> lock(mMetricsMutex);
181
182#ifdef VERY_VERBOSE_PRINTING
183    if (mPrintAllLogs) {
184        ALOGI("%s", event->ToString().c_str());
185    }
186#endif
187    const int64_t currentTimestampNs = event->GetElapsedTimestampNs();
188
189    if (reconnected && mLastTimestampSeen != 0) {
190        // LogReader tells us the connection has just been reset. Now we need
191        // to enter reconnection state to find the last CP.
192        mInReconnection = true;
193    }
194
195    if (mInReconnection) {
196        // We see the checkpoint
197        if (currentTimestampNs == mLastTimestampSeen) {
198            mInReconnection = false;
199            // Found the CP. ignore this event, and we will start to read from next event.
200            return;
201        }
202        if (currentTimestampNs > mLargestTimestampSeen) {
203            // We see a new log but CP has not been found yet. Give up now.
204            mLogLossCount++;
205            mInReconnection = false;
206            StatsdStats::getInstance().noteLogLost(currentTimestampNs);
207            // Persist the data before we reset. Do we want this?
208            WriteDataToDiskLocked(CONFIG_RESET);
209            // We see fresher event before we see the checkpoint. We might have lost data.
210            // The best we can do is to reset.
211            resetConfigsLocked(currentTimestampNs);
212        } else {
213            // Still in search of the CP. Keep going.
214            return;
215        }
216    }
217
218    mLogCount++;
219    mLastTimestampSeen = currentTimestampNs;
220    if (mLargestTimestampSeen < currentTimestampNs) {
221        mLargestTimestampSeen = currentTimestampNs;
222    }
223
224    resetIfConfigTtlExpiredLocked(currentTimestampNs);
225
226    StatsdStats::getInstance().noteAtomLogged(
227        event->GetTagId(), event->GetElapsedTimestampNs() / NS_PER_SEC);
228
229    // Hard-coded logic to update the isolated uid's in the uid-map.
230    // The field numbers need to be currently updated by hand with atoms.proto
231    if (event->GetTagId() == android::util::ISOLATED_UID_CHANGED) {
232        onIsolatedUidChangedEventLocked(*event);
233    }
234
235    if (mMetricsManagers.empty()) {
236        return;
237    }
238
239    int64_t curTimeSec = getElapsedRealtimeSec();
240    if (curTimeSec - mLastPullerCacheClearTimeSec > StatsdStats::kPullerCacheClearIntervalSec) {
241        mStatsPullerManager.ClearPullerCacheIfNecessary(curTimeSec * NS_PER_SEC);
242        mLastPullerCacheClearTimeSec = curTimeSec;
243    }
244
245
246    if (event->GetTagId() != android::util::ISOLATED_UID_CHANGED) {
247        // Map the isolated uid to host uid if necessary.
248        mapIsolatedUidToHostUidIfNecessaryLocked(event);
249    }
250
251    // pass the event to metrics managers.
252    for (auto& pair : mMetricsManagers) {
253        pair.second->onLogEvent(*event);
254        flushIfNecessaryLocked(event->GetElapsedTimestampNs(), pair.first, *(pair.second));
255    }
256}
257
258void StatsLogProcessor::OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key,
259                                        const StatsdConfig& config) {
260    std::lock_guard<std::mutex> lock(mMetricsMutex);
261    WriteDataToDiskLocked(key, timestampNs, CONFIG_UPDATED);
262    OnConfigUpdatedLocked(timestampNs, key, config);
263}
264
265void StatsLogProcessor::OnConfigUpdatedLocked(
266        const int64_t timestampNs, const ConfigKey& key, const StatsdConfig& config) {
267    VLOG("Updated configuration for key %s", key.ToString().c_str());
268    sp<MetricsManager> newMetricsManager =
269        new MetricsManager(key, config, mTimeBaseNs, timestampNs, mUidMap,
270                           mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
271    if (newMetricsManager->isConfigValid()) {
272        mUidMap->OnConfigUpdated(key);
273        if (newMetricsManager->shouldAddUidMapListener()) {
274            // We have to add listener after the MetricsManager is constructed because it's
275            // not safe to create wp or sp from this pointer inside its constructor.
276            mUidMap->addListener(newMetricsManager.get());
277        }
278        newMetricsManager->refreshTtl(timestampNs);
279        mMetricsManagers[key] = newMetricsManager;
280        VLOG("StatsdConfig valid");
281    } else {
282        // If there is any error in the config, don't use it.
283        ALOGE("StatsdConfig NOT valid");
284    }
285}
286
287size_t StatsLogProcessor::GetMetricsSize(const ConfigKey& key) const {
288    std::lock_guard<std::mutex> lock(mMetricsMutex);
289    auto it = mMetricsManagers.find(key);
290    if (it == mMetricsManagers.end()) {
291        ALOGW("Config source %s does not exist", key.ToString().c_str());
292        return 0;
293    }
294    return it->second->byteSize();
295}
296
297void StatsLogProcessor::dumpStates(FILE* out, bool verbose) {
298    std::lock_guard<std::mutex> lock(mMetricsMutex);
299    fprintf(out, "MetricsManager count: %lu\n", (unsigned long)mMetricsManagers.size());
300    for (auto metricsManager : mMetricsManagers) {
301        metricsManager.second->dumpStates(out, verbose);
302    }
303}
304
305/*
306 * onDumpReport dumps serialized ConfigMetricsReportList into outData.
307 */
308void StatsLogProcessor::onDumpReport(const ConfigKey& key, const int64_t dumpTimeStampNs,
309                                     const bool include_current_partial_bucket,
310                                     const DumpReportReason dumpReportReason,
311                                     vector<uint8_t>* outData) {
312    std::lock_guard<std::mutex> lock(mMetricsMutex);
313
314    ProtoOutputStream proto;
315
316    // Start of ConfigKey.
317    uint64_t configKeyToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY);
318    proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID, key.GetUid());
319    proto.write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)key.GetId());
320    proto.end(configKeyToken);
321    // End of ConfigKey.
322
323    // Then, check stats-data directory to see there's any file containing
324    // ConfigMetricsReport from previous shutdowns to concatenate to reports.
325    StorageManager::appendConfigMetricsReport(key, &proto);
326
327    auto it = mMetricsManagers.find(key);
328    if (it != mMetricsManagers.end()) {
329        // This allows another broadcast to be sent within the rate-limit period if we get close to
330        // filling the buffer again soon.
331        mLastBroadcastTimes.erase(key);
332
333        // Start of ConfigMetricsReport (reports).
334        uint64_t reportsToken =
335                proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS);
336        onConfigMetricsReportLocked(key, dumpTimeStampNs, include_current_partial_bucket,
337                                    dumpReportReason, &proto);
338        proto.end(reportsToken);
339        // End of ConfigMetricsReport (reports).
340    } else {
341        ALOGW("Config source %s does not exist", key.ToString().c_str());
342    }
343
344    if (outData != nullptr) {
345        outData->clear();
346        outData->resize(proto.size());
347        size_t pos = 0;
348        auto iter = proto.data();
349        while (iter.readBuffer() != NULL) {
350            size_t toRead = iter.currentToRead();
351            std::memcpy(&((*outData)[pos]), iter.readBuffer(), toRead);
352            pos += toRead;
353            iter.rp()->move(toRead);
354        }
355    }
356
357    StatsdStats::getInstance().noteMetricsReportSent(key, proto.size());
358}
359
360/*
361 * onConfigMetricsReportLocked dumps serialized ConfigMetricsReport into outData.
362 */
363void StatsLogProcessor::onConfigMetricsReportLocked(const ConfigKey& key,
364                                                    const int64_t dumpTimeStampNs,
365                                                    const bool include_current_partial_bucket,
366                                                    const DumpReportReason dumpReportReason,
367                                                    ProtoOutputStream* proto) {
368    // We already checked whether key exists in mMetricsManagers in
369    // WriteDataToDisk.
370    auto it = mMetricsManagers.find(key);
371    if (it == mMetricsManagers.end()) {
372        return;
373    }
374    int64_t lastReportTimeNs = it->second->getLastReportTimeNs();
375    int64_t lastReportWallClockNs = it->second->getLastReportWallClockNs();
376
377    std::set<string> str_set;
378
379    // First, fill in ConfigMetricsReport using current data on memory, which
380    // starts from filling in StatsLogReport's.
381    it->second->onDumpReport(dumpTimeStampNs, include_current_partial_bucket,
382                             &str_set, proto);
383
384    // Fill in UidMap if there is at least one metric to report.
385    // This skips the uid map if it's an empty config.
386    if (it->second->getNumMetrics() > 0) {
387        uint64_t uidMapToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP);
388        if (it->second->hashStringInReport()) {
389            mUidMap->appendUidMap(dumpTimeStampNs, key, &str_set, proto);
390        } else {
391            mUidMap->appendUidMap(dumpTimeStampNs, key, nullptr, proto);
392        }
393        proto->end(uidMapToken);
394    }
395
396    // Fill in the timestamps.
397    proto->write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_ELAPSED_NANOS,
398                (long long)lastReportTimeNs);
399    proto->write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS,
400                (long long)dumpTimeStampNs);
401    proto->write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_WALL_CLOCK_NANOS,
402                (long long)lastReportWallClockNs);
403    proto->write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_WALL_CLOCK_NANOS,
404                (long long)getWallClockNs());
405    // Dump report reason
406    proto->write(FIELD_TYPE_INT32 | FIELD_ID_DUMP_REPORT_REASON, dumpReportReason);
407
408    for (const auto& str : str_set) {
409        proto->write(FIELD_TYPE_STRING | FIELD_COUNT_REPEATED | FIELD_ID_STRINGS, str);
410    }
411}
412
413void StatsLogProcessor::resetConfigsLocked(const int64_t timestampNs,
414                                           const std::vector<ConfigKey>& configs) {
415    for (const auto& key : configs) {
416        StatsdConfig config;
417        if (StorageManager::readConfigFromDisk(key, &config)) {
418            OnConfigUpdatedLocked(timestampNs, key, config);
419            StatsdStats::getInstance().noteConfigReset(key);
420        } else {
421            ALOGE("Failed to read backup config from disk for : %s", key.ToString().c_str());
422            auto it = mMetricsManagers.find(key);
423            if (it != mMetricsManagers.end()) {
424                it->second->refreshTtl(timestampNs);
425            }
426        }
427    }
428}
429
430void StatsLogProcessor::resetIfConfigTtlExpiredLocked(const int64_t timestampNs) {
431    std::vector<ConfigKey> configKeysTtlExpired;
432    for (auto it = mMetricsManagers.begin(); it != mMetricsManagers.end(); it++) {
433        if (it->second != nullptr && !it->second->isInTtl(timestampNs)) {
434            configKeysTtlExpired.push_back(it->first);
435        }
436    }
437    if (configKeysTtlExpired.size() > 0) {
438        WriteDataToDiskLocked(CONFIG_RESET);
439        resetConfigsLocked(timestampNs, configKeysTtlExpired);
440    }
441}
442
443void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
444    std::lock_guard<std::mutex> lock(mMetricsMutex);
445    auto it = mMetricsManagers.find(key);
446    if (it != mMetricsManagers.end()) {
447        WriteDataToDiskLocked(key, getElapsedRealtimeNs(), CONFIG_REMOVED);
448        mMetricsManagers.erase(it);
449        mUidMap->OnConfigRemoved(key);
450    }
451    StatsdStats::getInstance().noteConfigRemoved(key);
452
453    mLastBroadcastTimes.erase(key);
454
455    if (mMetricsManagers.empty()) {
456        mStatsPullerManager.ForceClearPullerCache();
457    }
458}
459
460void StatsLogProcessor::flushIfNecessaryLocked(
461    int64_t timestampNs, const ConfigKey& key, MetricsManager& metricsManager) {
462    auto lastCheckTime = mLastByteSizeTimes.find(key);
463    if (lastCheckTime != mLastByteSizeTimes.end()) {
464        if (timestampNs - lastCheckTime->second < StatsdStats::kMinByteSizeCheckPeriodNs) {
465            return;
466        }
467    }
468
469    // We suspect that the byteSize() computation is expensive, so we set a rate limit.
470    size_t totalBytes = metricsManager.byteSize();
471    mLastByteSizeTimes[key] = timestampNs;
472    bool requestDump = false;
473    if (totalBytes >
474        StatsdStats::kMaxMetricsBytesPerConfig) {  // Too late. We need to start clearing data.
475        metricsManager.dropData(timestampNs);
476        StatsdStats::getInstance().noteDataDropped(key);
477        VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str());
478    } else if ((totalBytes > StatsdStats::kBytesPerConfigTriggerGetData) ||
479               (mOnDiskDataConfigs.find(key) != mOnDiskDataConfigs.end())) {
480        // Request to send a broadcast if:
481        // 1. in memory data > threshold   OR
482        // 2. config has old data report on disk.
483        requestDump = true;
484    }
485
486    if (requestDump) {
487        // Send broadcast so that receivers can pull data.
488        auto lastBroadcastTime = mLastBroadcastTimes.find(key);
489        if (lastBroadcastTime != mLastBroadcastTimes.end()) {
490            if (timestampNs - lastBroadcastTime->second < StatsdStats::kMinBroadcastPeriodNs) {
491                VLOG("StatsD would've sent a broadcast but the rate limit stopped us.");
492                return;
493            }
494        }
495        if (mSendBroadcast(key)) {
496            mOnDiskDataConfigs.erase(key);
497            VLOG("StatsD triggered data fetch for %s", key.ToString().c_str());
498            mLastBroadcastTimes[key] = timestampNs;
499            StatsdStats::getInstance().noteBroadcastSent(key);
500        }
501    }
502}
503
504void StatsLogProcessor::WriteDataToDiskLocked(const ConfigKey& key,
505                                              const int64_t timestampNs,
506                                              const DumpReportReason dumpReportReason) {
507    if (mMetricsManagers.find(key) == mMetricsManagers.end() ||
508        !mMetricsManagers.find(key)->second->shouldWriteToDisk()) {
509        return;
510    }
511    ProtoOutputStream proto;
512    onConfigMetricsReportLocked(key, timestampNs, true /* include_current_partial_bucket*/,
513                                dumpReportReason, &proto);
514    string file_name = StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR,
515         (long)getWallClockSec(), key.GetUid(), (long long)key.GetId());
516    android::base::unique_fd fd(open(file_name.c_str(),
517                                O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR));
518    if (fd == -1) {
519        ALOGE("Attempt to write %s but failed", file_name.c_str());
520        return;
521    }
522    proto.flush(fd.get());
523    // We were able to write the ConfigMetricsReport to disk, so we should trigger collection ASAP.
524    mOnDiskDataConfigs.insert(key);
525}
526
527void StatsLogProcessor::WriteDataToDiskLocked(const DumpReportReason dumpReportReason) {
528    const int64_t timeNs = getElapsedRealtimeNs();
529    for (auto& pair : mMetricsManagers) {
530        WriteDataToDiskLocked(pair.first, timeNs, dumpReportReason);
531    }
532}
533
534void StatsLogProcessor::WriteDataToDisk(const DumpReportReason dumpReportReason) {
535    std::lock_guard<std::mutex> lock(mMetricsMutex);
536    WriteDataToDiskLocked(dumpReportReason);
537}
538
539void StatsLogProcessor::informPullAlarmFired(const int64_t timestampNs) {
540    std::lock_guard<std::mutex> lock(mMetricsMutex);
541    mStatsPullerManager.OnAlarmFired(timestampNs);
542}
543
544int64_t StatsLogProcessor::getLastReportTimeNs(const ConfigKey& key) {
545    auto it = mMetricsManagers.find(key);
546    if (it == mMetricsManagers.end()) {
547        return 0;
548    } else {
549        return it->second->getLastReportTimeNs();
550    }
551}
552
553void StatsLogProcessor::noteOnDiskData(const ConfigKey& key) {
554    std::lock_guard<std::mutex> lock(mMetricsMutex);
555    mOnDiskDataConfigs.insert(key);
556}
557
558}  // namespace statsd
559}  // namespace os
560}  // namespace android
561