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 STATSD_DEBUG false  // STOPSHIP if true
17 #if !defined(NDEBUG) && !defined(DEBUG)
18 #define NDEBUG  // comment to enable assert
19 #endif          /* !defined(NDEBUG) && !defined(DEBUG) */
20 #include "Log.h"
21 
22 #include "MetricsManager.h"
23 
24 #include <assert.h>
25 #include <private/android_filesystem_config.h>
26 
27 #include "CountMetricProducer.h"
28 #include "condition/CombinationConditionTracker.h"
29 #include "condition/SimpleConditionTracker.h"
30 #include "flags/FlagProvider.h"
31 #include "guardrail/StatsdStats.h"
32 #include "matchers/CombinationAtomMatchingTracker.h"
33 #include "matchers/SimpleAtomMatchingTracker.h"
34 #include "parsing_utils/config_update_utils.h"
35 #include "parsing_utils/metrics_manager_util.h"
36 #include "state/StateManager.h"
37 #include "stats_log_util.h"
38 #include "stats_util.h"
39 #include "statslog_statsd.h"
40 #include "utils/DbUtils.h"
41 #include "utils/api_tracing.h"
42 
43 using android::util::FIELD_COUNT_REPEATED;
44 using android::util::FIELD_TYPE_INT32;
45 using android::util::FIELD_TYPE_INT64;
46 using android::util::FIELD_TYPE_MESSAGE;
47 using android::util::FIELD_TYPE_STRING;
48 using android::util::ProtoOutputStream;
49 
50 using std::set;
51 using std::string;
52 using std::unique_ptr;
53 using std::vector;
54 
55 namespace android {
56 namespace os {
57 namespace statsd {
58 
59 const int FIELD_ID_METRICS = 1;
60 const int FIELD_ID_ANNOTATIONS = 7;
61 const int FIELD_ID_ANNOTATIONS_INT64 = 1;
62 const int FIELD_ID_ANNOTATIONS_INT32 = 2;
63 
64 // for ActiveConfig
65 const int FIELD_ID_ACTIVE_CONFIG_ID = 1;
66 const int FIELD_ID_ACTIVE_CONFIG_UID = 2;
67 const int FIELD_ID_ACTIVE_CONFIG_METRIC = 3;
68 
MetricsManager(const ConfigKey & key,const StatsdConfig & config,const int64_t timeBaseNs,const int64_t currentTimeNs,const sp<UidMap> & uidMap,const sp<StatsPullerManager> & pullerManager,const sp<AlarmMonitor> & anomalyAlarmMonitor,const sp<AlarmMonitor> & periodicAlarmMonitor)69 MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config,
70                                const int64_t timeBaseNs, const int64_t currentTimeNs,
71                                const sp<UidMap>& uidMap,
72                                const sp<StatsPullerManager>& pullerManager,
73                                const sp<AlarmMonitor>& anomalyAlarmMonitor,
74                                const sp<AlarmMonitor>& periodicAlarmMonitor)
75     : mConfigKey(key),
76       mUidMap(uidMap),
77       mPackageCertificateHashSizeBytes(
78               static_cast<uint8_t>(config.package_certificate_hash_size_bytes())),
79       mTtlNs(config.has_ttl_in_seconds() ? config.ttl_in_seconds() * NS_PER_SEC : -1),
80       mTtlEndNs(-1),
81       mLastReportTimeNs(currentTimeNs),
82       mLastReportWallClockNs(getWallClockNs()),
83       mPullerManager(pullerManager),
84       mWhitelistedAtomIds(config.whitelisted_atom_ids().begin(),
85                           config.whitelisted_atom_ids().end()),
86       mShouldPersistHistory(config.persist_locally()),
87       mUseV2SoftMemoryCalculation(config.statsd_config_options().use_v2_soft_memory_limit()),
88       mOmitSystemUidsInUidMap(config.statsd_config_options().omit_system_uids_in_uidmap()) {
89     if (!isAtLeastU() && config.has_restricted_metrics_delegate_package_name()) {
90         mInvalidConfigReason =
91                 InvalidConfigReason(INVALID_CONFIG_REASON_RESTRICTED_METRIC_NOT_ENABLED);
92         return;
93     }
94     if (config.has_restricted_metrics_delegate_package_name()) {
95         mRestrictedMetricsDelegatePackageName = config.restricted_metrics_delegate_package_name();
96     }
97     // Init the ttl end timestamp.
98     refreshTtl(timeBaseNs);
99     mInvalidConfigReason = initStatsdConfig(
100             key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
101             timeBaseNs, currentTimeNs, this, mTagIdsToMatchersMap, mAllAtomMatchingTrackers,
102             mAtomMatchingTrackerMap, mAllConditionTrackers, mConditionTrackerMap,
103             mAllMetricProducers, mMetricProducerMap, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers,
104             mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap,
105             mActivationAtomTrackerToMetricMap, mDeactivationAtomTrackerToMetricMap,
106             mAlertTrackerMap, mMetricIndexesWithActivation, mStateProtoHashes, mNoReportMetricIds);
107 
108     mHashStringsInReport = config.hash_strings_in_metric_report();
109     mVersionStringsInReport = config.version_strings_in_metric_report();
110     mInstallerInReport = config.installer_in_metric_report();
111 
112     createAllLogSourcesFromConfig(config);
113     setMaxMetricsBytesFromConfig(config);
114     setTriggerGetDataBytesFromConfig(config);
115     mPullerManager->RegisterPullUidProvider(mConfigKey, this);
116 
117     // Store the sub-configs used.
118     for (const auto& annotation : config.annotation()) {
119         mAnnotations.emplace_back(annotation.field_int64(), annotation.field_int32());
120     }
121     verifyGuardrailsAndUpdateStatsdStats();
122     initializeConfigActiveStatus();
123 }
124 
~MetricsManager()125 MetricsManager::~MetricsManager() {
126     for (auto it : mAllMetricProducers) {
127         for (int atomId : it->getSlicedStateAtoms()) {
128             StateManager::getInstance().unregisterListener(atomId, it);
129         }
130     }
131     mPullerManager->UnregisterPullUidProvider(mConfigKey, this);
132 
133     VLOG("~MetricsManager()");
134 }
135 
updateConfig(const StatsdConfig & config,const int64_t timeBaseNs,const int64_t currentTimeNs,const sp<AlarmMonitor> & anomalyAlarmMonitor,const sp<AlarmMonitor> & periodicAlarmMonitor)136 bool MetricsManager::updateConfig(const StatsdConfig& config, const int64_t timeBaseNs,
137                                   const int64_t currentTimeNs,
138                                   const sp<AlarmMonitor>& anomalyAlarmMonitor,
139                                   const sp<AlarmMonitor>& periodicAlarmMonitor) {
140     if (!isAtLeastU() && config.has_restricted_metrics_delegate_package_name()) {
141         mInvalidConfigReason =
142                 InvalidConfigReason(INVALID_CONFIG_REASON_RESTRICTED_METRIC_NOT_ENABLED);
143         return false;
144     }
145     if (config.has_restricted_metrics_delegate_package_name()) {
146         mRestrictedMetricsDelegatePackageName = config.restricted_metrics_delegate_package_name();
147     } else {
148         mRestrictedMetricsDelegatePackageName = nullopt;
149     }
150     vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers;
151     unordered_map<int64_t, int> newAtomMatchingTrackerMap;
152     vector<sp<ConditionTracker>> newConditionTrackers;
153     unordered_map<int64_t, int> newConditionTrackerMap;
154     map<int64_t, uint64_t> newStateProtoHashes;
155     vector<sp<MetricProducer>> newMetricProducers;
156     unordered_map<int64_t, int> newMetricProducerMap;
157     vector<sp<AnomalyTracker>> newAnomalyTrackers;
158     unordered_map<int64_t, int> newAlertTrackerMap;
159     vector<sp<AlarmTracker>> newPeriodicAlarmTrackers;
160     mTagIdsToMatchersMap.clear();
161     mConditionToMetricMap.clear();
162     mTrackerToMetricMap.clear();
163     mTrackerToConditionMap.clear();
164     mActivationAtomTrackerToMetricMap.clear();
165     mDeactivationAtomTrackerToMetricMap.clear();
166     mMetricIndexesWithActivation.clear();
167     mNoReportMetricIds.clear();
168     mInvalidConfigReason = updateStatsdConfig(
169             mConfigKey, config, mUidMap, mPullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
170             timeBaseNs, currentTimeNs, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap,
171             mAllConditionTrackers, mConditionTrackerMap, mAllMetricProducers, mMetricProducerMap,
172             mAllAnomalyTrackers, mAlertTrackerMap, mStateProtoHashes, this, mTagIdsToMatchersMap,
173             newAtomMatchingTrackers, newAtomMatchingTrackerMap, newConditionTrackers,
174             newConditionTrackerMap, newMetricProducers, newMetricProducerMap, newAnomalyTrackers,
175             newAlertTrackerMap, newPeriodicAlarmTrackers, mConditionToMetricMap,
176             mTrackerToMetricMap, mTrackerToConditionMap, mActivationAtomTrackerToMetricMap,
177             mDeactivationAtomTrackerToMetricMap, mMetricIndexesWithActivation, newStateProtoHashes,
178             mNoReportMetricIds);
179     mAllAtomMatchingTrackers = newAtomMatchingTrackers;
180     mAtomMatchingTrackerMap = newAtomMatchingTrackerMap;
181     mAllConditionTrackers = newConditionTrackers;
182     mConditionTrackerMap = newConditionTrackerMap;
183     mAllMetricProducers = newMetricProducers;
184     mMetricProducerMap = newMetricProducerMap;
185     mStateProtoHashes = newStateProtoHashes;
186     mAllAnomalyTrackers = newAnomalyTrackers;
187     mAlertTrackerMap = newAlertTrackerMap;
188     mAllPeriodicAlarmTrackers = newPeriodicAlarmTrackers;
189 
190     mTtlNs = config.has_ttl_in_seconds() ? config.ttl_in_seconds() * NS_PER_SEC : -1;
191     refreshTtl(currentTimeNs);
192 
193     mHashStringsInReport = config.hash_strings_in_metric_report();
194     mVersionStringsInReport = config.version_strings_in_metric_report();
195     mInstallerInReport = config.installer_in_metric_report();
196     mWhitelistedAtomIds.clear();
197     mWhitelistedAtomIds.insert(config.whitelisted_atom_ids().begin(),
198                                config.whitelisted_atom_ids().end());
199     mShouldPersistHistory = config.persist_locally();
200     mPackageCertificateHashSizeBytes = config.package_certificate_hash_size_bytes();
201     mUseV2SoftMemoryCalculation = config.statsd_config_options().use_v2_soft_memory_limit();
202     mOmitSystemUidsInUidMap = config.statsd_config_options().omit_system_uids_in_uidmap();
203 
204     // Store the sub-configs used.
205     mAnnotations.clear();
206     for (const auto& annotation : config.annotation()) {
207         mAnnotations.emplace_back(annotation.field_int64(), annotation.field_int32());
208     }
209 
210     mAllowedUid.clear();
211     mAllowedPkg.clear();
212     mDefaultPullUids.clear();
213     mPullAtomUids.clear();
214     mPullAtomPackages.clear();
215     createAllLogSourcesFromConfig(config);
216     setMaxMetricsBytesFromConfig(config);
217     setTriggerGetDataBytesFromConfig(config);
218 
219     verifyGuardrailsAndUpdateStatsdStats();
220     initializeConfigActiveStatus();
221     return !mInvalidConfigReason.has_value();
222 }
223 
createAllLogSourcesFromConfig(const StatsdConfig & config)224 void MetricsManager::createAllLogSourcesFromConfig(const StatsdConfig& config) {
225     // Init allowed pushed atom uids.
226     for (const auto& source : config.allowed_log_source()) {
227         auto it = UidMap::sAidToUidMapping.find(source);
228         if (it != UidMap::sAidToUidMapping.end()) {
229             mAllowedUid.push_back(it->second);
230         } else {
231             mAllowedPkg.push_back(source);
232         }
233     }
234 
235     if (mAllowedUid.size() + mAllowedPkg.size() > StatsdStats::kMaxLogSourceCount) {
236         ALOGE("Too many log sources. This is likely to be an error in the config.");
237         mInvalidConfigReason = InvalidConfigReason(INVALID_CONFIG_REASON_TOO_MANY_LOG_SOURCES);
238     } else {
239         initAllowedLogSources();
240     }
241 
242     // Init default allowed pull atom uids.
243     int numPullPackages = 0;
244     for (const string& pullSource : config.default_pull_packages()) {
245         auto it = UidMap::sAidToUidMapping.find(pullSource);
246         if (it != UidMap::sAidToUidMapping.end()) {
247             numPullPackages++;
248             mDefaultPullUids.insert(it->second);
249         } else {
250             ALOGE("Default pull atom packages must be in sAidToUidMapping");
251             mInvalidConfigReason =
252                     InvalidConfigReason(INVALID_CONFIG_REASON_DEFAULT_PULL_PACKAGES_NOT_IN_MAP);
253         }
254     }
255     // Init per-atom pull atom packages.
256     for (const PullAtomPackages& pullAtomPackages : config.pull_atom_packages()) {
257         int32_t atomId = pullAtomPackages.atom_id();
258         for (const string& pullPackage : pullAtomPackages.packages()) {
259             numPullPackages++;
260             auto it = UidMap::sAidToUidMapping.find(pullPackage);
261             if (it != UidMap::sAidToUidMapping.end()) {
262                 mPullAtomUids[atomId].insert(it->second);
263             } else {
264                 mPullAtomPackages[atomId].insert(pullPackage);
265             }
266         }
267     }
268     if (numPullPackages > StatsdStats::kMaxPullAtomPackages) {
269         ALOGE("Too many sources in default_pull_packages and pull_atom_packages. This is likely to "
270               "be an error in the config");
271         mInvalidConfigReason =
272                 InvalidConfigReason(INVALID_CONFIG_REASON_TOO_MANY_SOURCES_IN_PULL_PACKAGES);
273     } else {
274         initPullAtomSources();
275     }
276 }
277 
setMaxMetricsBytesFromConfig(const StatsdConfig & config)278 void MetricsManager::setMaxMetricsBytesFromConfig(const StatsdConfig& config) {
279     if (!config.has_max_metrics_memory_kb()) {
280         mMaxMetricsBytes = StatsdStats::kDefaultMaxMetricsBytesPerConfig;
281         return;
282     }
283     if (config.max_metrics_memory_kb() <= 0 ||
284         static_cast<size_t>(config.max_metrics_memory_kb() * 1024) >
285                 StatsdStats::kHardMaxMetricsBytesPerConfig) {
286         ALOGW("Memory limit must be between 0KB and 20MB. Setting to default value (2MB).");
287         mMaxMetricsBytes = StatsdStats::kDefaultMaxMetricsBytesPerConfig;
288     } else {
289         mMaxMetricsBytes = config.max_metrics_memory_kb() * 1024;
290     }
291 }
292 
setTriggerGetDataBytesFromConfig(const StatsdConfig & config)293 void MetricsManager::setTriggerGetDataBytesFromConfig(const StatsdConfig& config) {
294     if (!config.has_soft_metrics_memory_kb()) {
295         mTriggerGetDataBytes = StatsdStats::kDefaultBytesPerConfigTriggerGetData;
296         return;
297     }
298     if (config.soft_metrics_memory_kb() <= 0 ||
299         static_cast<size_t>(config.soft_metrics_memory_kb() * 1024) >
300                 StatsdStats::kHardMaxTriggerGetDataBytes) {
301         ALOGW("Memory limit ust be between 0KB and 10MB. Setting to default value (192KB).");
302         mTriggerGetDataBytes = StatsdStats::kDefaultBytesPerConfigTriggerGetData;
303     } else {
304         mTriggerGetDataBytes = config.soft_metrics_memory_kb() * 1024;
305     }
306 }
307 
verifyGuardrailsAndUpdateStatsdStats()308 void MetricsManager::verifyGuardrailsAndUpdateStatsdStats() {
309     // Guardrail. Reject the config if it's too big.
310     if (mAllMetricProducers.size() > StatsdStats::kMaxMetricCountPerConfig) {
311         ALOGE("This config has too many metrics! Reject!");
312         mInvalidConfigReason = InvalidConfigReason(INVALID_CONFIG_REASON_TOO_MANY_METRICS);
313     }
314     if (mAllConditionTrackers.size() > StatsdStats::kMaxConditionCountPerConfig) {
315         ALOGE("This config has too many predicates! Reject!");
316         mInvalidConfigReason = InvalidConfigReason(INVALID_CONFIG_REASON_TOO_MANY_CONDITIONS);
317     }
318     if (mAllAtomMatchingTrackers.size() > StatsdStats::kMaxMatcherCountPerConfig) {
319         ALOGE("This config has too many matchers! Reject!");
320         mInvalidConfigReason = InvalidConfigReason(INVALID_CONFIG_REASON_TOO_MANY_MATCHERS);
321     }
322     if (mAllAnomalyTrackers.size() > StatsdStats::kMaxAlertCountPerConfig) {
323         ALOGE("This config has too many alerts! Reject!");
324         mInvalidConfigReason = InvalidConfigReason(INVALID_CONFIG_REASON_TOO_MANY_ALERTS);
325     }
326     // no matter whether this config is valid, log it in the stats.
327     StatsdStats::getInstance().noteConfigReceived(
328             mConfigKey, mAllMetricProducers.size(), mAllConditionTrackers.size(),
329             mAllAtomMatchingTrackers.size(), mAllAnomalyTrackers.size(), mAnnotations,
330             mInvalidConfigReason);
331 }
332 
initializeConfigActiveStatus()333 void MetricsManager::initializeConfigActiveStatus() {
334     mIsAlwaysActive = (mMetricIndexesWithActivation.size() != mAllMetricProducers.size()) ||
335                       (mAllMetricProducers.size() == 0);
336     mIsActive = mIsAlwaysActive;
337     for (int metric : mMetricIndexesWithActivation) {
338         mIsActive |= mAllMetricProducers[metric]->isActive();
339     }
340     VLOG("mIsActive is initialized to %d", mIsActive);
341 }
342 
initAllowedLogSources()343 void MetricsManager::initAllowedLogSources() {
344     std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
345     mAllowedLogSources.clear();
346     mAllowedLogSources.insert(mAllowedUid.begin(), mAllowedUid.end());
347 
348     for (const auto& pkg : mAllowedPkg) {
349         auto uids = mUidMap->getAppUid(pkg);
350         mAllowedLogSources.insert(uids.begin(), uids.end());
351     }
352     if (STATSD_DEBUG) {
353         for (const auto& uid : mAllowedLogSources) {
354             VLOG("Allowed uid %d", uid);
355         }
356     }
357 }
358 
initPullAtomSources()359 void MetricsManager::initPullAtomSources() {
360     std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
361     mCombinedPullAtomUids.clear();
362     for (const auto& [atomId, uids] : mPullAtomUids) {
363         mCombinedPullAtomUids[atomId].insert(uids.begin(), uids.end());
364     }
365     for (const auto& [atomId, packages] : mPullAtomPackages) {
366         for (const string& pkg : packages) {
367             set<int32_t> uids = mUidMap->getAppUid(pkg);
368             mCombinedPullAtomUids[atomId].insert(uids.begin(), uids.end());
369         }
370     }
371 }
372 
isConfigValid() const373 bool MetricsManager::isConfigValid() const {
374     return !mInvalidConfigReason.has_value();
375 }
376 
notifyAppUpgrade(const int64_t eventTimeNs,const string & apk,const int uid,const int64_t version)377 void MetricsManager::notifyAppUpgrade(const int64_t eventTimeNs, const string& apk, const int uid,
378                                       const int64_t version) {
379     // Inform all metric producers.
380     for (const auto& it : mAllMetricProducers) {
381         it->notifyAppUpgrade(eventTimeNs);
382     }
383     // check if we care this package
384     if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) {
385         // We will re-initialize the whole list because we don't want to keep the multi mapping of
386         // UID<->pkg inside MetricsManager to reduce the memory usage.
387         initAllowedLogSources();
388     }
389 
390     for (const auto& it : mPullAtomPackages) {
391         if (it.second.find(apk) != it.second.end()) {
392             initPullAtomSources();
393             return;
394         }
395     }
396 }
397 
notifyAppRemoved(const int64_t eventTimeNs,const string & apk,const int uid)398 void MetricsManager::notifyAppRemoved(const int64_t eventTimeNs, const string& apk, const int uid) {
399     // Inform all metric producers.
400     for (const auto& it : mAllMetricProducers) {
401         it->notifyAppRemoved(eventTimeNs);
402     }
403     // check if we care this package
404     if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) {
405         // We will re-initialize the whole list because we don't want to keep the multi mapping of
406         // UID<->pkg inside MetricsManager to reduce the memory usage.
407         initAllowedLogSources();
408     }
409 
410     for (const auto& it : mPullAtomPackages) {
411         if (it.second.find(apk) != it.second.end()) {
412             initPullAtomSources();
413             return;
414         }
415     }
416 }
417 
onUidMapReceived(const int64_t eventTimeNs)418 void MetricsManager::onUidMapReceived(const int64_t eventTimeNs) {
419     // Purposefully don't inform metric producers on a new snapshot
420     // because we don't need to flush partial buckets.
421     // This occurs if a new user is added/removed or statsd crashes.
422     initPullAtomSources();
423 
424     if (mAllowedPkg.size() == 0) {
425         return;
426     }
427     initAllowedLogSources();
428 }
429 
onStatsdInitCompleted(const int64_t eventTimeNs)430 void MetricsManager::onStatsdInitCompleted(const int64_t eventTimeNs) {
431     ATRACE_CALL();
432     // Inform all metric producers.
433     for (const auto& it : mAllMetricProducers) {
434         it->onStatsdInitCompleted(eventTimeNs);
435     }
436 }
437 
init()438 void MetricsManager::init() {
439     for (const auto& producer : mAllMetricProducers) {
440         producer->prepareFirstBucket();
441     }
442 }
443 
getPullAtomUids(int32_t atomId)444 vector<int32_t> MetricsManager::getPullAtomUids(int32_t atomId) {
445     std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
446     vector<int32_t> uids;
447     const auto& it = mCombinedPullAtomUids.find(atomId);
448     if (it != mCombinedPullAtomUids.end()) {
449         uids.insert(uids.end(), it->second.begin(), it->second.end());
450     }
451     uids.insert(uids.end(), mDefaultPullUids.begin(), mDefaultPullUids.end());
452     return uids;
453 }
454 
useV2SoftMemoryCalculation()455 bool MetricsManager::useV2SoftMemoryCalculation() {
456     return mUseV2SoftMemoryCalculation;
457 }
458 
dumpStates(int out,bool verbose)459 void MetricsManager::dumpStates(int out, bool verbose) {
460     dprintf(out, "ConfigKey %s, allowed source:", mConfigKey.ToString().c_str());
461     {
462         std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
463         for (const auto& source : mAllowedLogSources) {
464             dprintf(out, "%d ", source);
465         }
466     }
467     dprintf(out, "\n");
468     for (const auto& producer : mAllMetricProducers) {
469         producer->dumpStates(out, verbose);
470     }
471 }
472 
dropData(const int64_t dropTimeNs)473 void MetricsManager::dropData(const int64_t dropTimeNs) {
474     for (const auto& producer : mAllMetricProducers) {
475         producer->dropData(dropTimeNs);
476     }
477 }
478 
onDumpReport(const int64_t dumpTimeStampNs,const int64_t wallClockNs,const bool include_current_partial_bucket,const bool erase_data,const DumpLatency dumpLatency,std::set<string> * str_set,ProtoOutputStream * protoOutput)479 void MetricsManager::onDumpReport(const int64_t dumpTimeStampNs, const int64_t wallClockNs,
480                                   const bool include_current_partial_bucket, const bool erase_data,
481                                   const DumpLatency dumpLatency, std::set<string>* str_set,
482                                   ProtoOutputStream* protoOutput) {
483     if (hasRestrictedMetricsDelegate()) {
484         // TODO(b/268150038): report error to statsdstats
485         VLOG("Unexpected call to onDumpReport in restricted metricsmanager.");
486         return;
487     }
488 
489     vector<std::pair<int32_t, int32_t>> queueOverflowStats =
490             StatsdStats::getInstance().getQueueOverflowAtomsStats();
491     processQueueOverflowStats(queueOverflowStats);
492 
493     VLOG("=========================Metric Reports Start==========================");
494     // one StatsLogReport per MetricProduer
495     for (const auto& producer : mAllMetricProducers) {
496         if (mNoReportMetricIds.find(producer->getMetricId()) == mNoReportMetricIds.end()) {
497             uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
498                                                 FIELD_ID_METRICS);
499             if (mHashStringsInReport) {
500                 producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, erase_data,
501                                        dumpLatency, str_set, protoOutput);
502             } else {
503                 producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, erase_data,
504                                        dumpLatency, nullptr, protoOutput);
505             }
506             protoOutput->end(token);
507         } else {
508             producer->clearPastBuckets(dumpTimeStampNs);
509         }
510     }
511     for (const auto& annotation : mAnnotations) {
512         uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
513                                             FIELD_ID_ANNOTATIONS);
514         protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ANNOTATIONS_INT64,
515                            (long long)annotation.first);
516         protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_ANNOTATIONS_INT32, annotation.second);
517         protoOutput->end(token);
518     }
519 
520     // Do not update the timestamps when data is not cleared to avoid timestamps from being
521     // misaligned.
522     if (erase_data) {
523         mLastReportTimeNs = dumpTimeStampNs;
524         mLastReportWallClockNs = wallClockNs;
525     }
526     VLOG("=========================Metric Reports End==========================");
527 }
528 
checkLogCredentials(const int32_t uid,const int32_t atomId) const529 bool MetricsManager::checkLogCredentials(const int32_t uid, const int32_t atomId) const {
530     if (mWhitelistedAtomIds.find(atomId) != mWhitelistedAtomIds.end()) {
531         return true;
532     }
533 
534     if (uid == AID_ROOT || (uid >= AID_SYSTEM && uid < AID_SHELL)) {
535         // enable atoms logged from pre-installed Android system services
536         return true;
537     }
538 
539     std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
540     if (mAllowedLogSources.find(uid) == mAllowedLogSources.end()) {
541         VLOG("log source %d not on the whitelist", uid);
542         return false;
543     }
544     return true;
545 }
546 
547 // Consume the stats log if it's interesting to this metric.
onLogEvent(const LogEvent & event)548 void MetricsManager::onLogEvent(const LogEvent& event) {
549     if (!isConfigValid()) {
550         return;
551     }
552 
553     const int tagId = event.GetTagId();
554 
555     if (tagId == util::STATS_SOCKET_LOSS_REPORTED) {
556         // Hard coded logic to handle socket loss info to highlight metric corruption reason
557         // STATS_SOCKET_LOSS_REPORTED might not be part of atoms allow list - but some of lost
558         // atoms can be always allowed - that is the reason to evaluate SocketLossInfo content prior
559         // the checkLogCredentials below
560         const std::optional<SocketLossInfo>& lossInfo = toSocketLossInfo(event);
561         if (lossInfo) {
562             onLogEventLost(*lossInfo);
563         }
564         // next, atom is going to be propagated to be consumed by metrics if any
565     }
566 
567     if (!checkLogCredentials(event)) {
568         return;
569     }
570 
571     const int64_t eventTimeNs = event.GetElapsedTimestampNs();
572 
573     bool isActive = mIsAlwaysActive;
574 
575     // Set of metrics that are still active after flushing.
576     unordered_set<int> activeMetricsIndices;
577 
578     // Update state of all metrics w/ activation conditions as of eventTimeNs.
579     for (int metricIndex : mMetricIndexesWithActivation) {
580         const sp<MetricProducer>& metric = mAllMetricProducers[metricIndex];
581         metric->flushIfExpire(eventTimeNs);
582         if (metric->isActive()) {
583             // If this metric w/ activation condition is still active after
584             // flushing, remember it.
585             activeMetricsIndices.insert(metricIndex);
586         }
587     }
588 
589     mIsActive = isActive || !activeMetricsIndices.empty();
590 
591     const auto matchersIt = mTagIdsToMatchersMap.find(tagId);
592 
593     if (matchersIt == mTagIdsToMatchersMap.end()) {
594         // Not interesting...
595         return;
596     }
597 
598     if (event.isParsedHeaderOnly()) {
599         // This should not happen if metric config is defined for certain atom id
600         const int64_t firstMatcherId =
601                 mAllAtomMatchingTrackers[*matchersIt->second.begin()]->getId();
602         ALOGW("Atom %d is mistakenly skipped - there is a matcher %lld for it", tagId,
603               (long long)firstMatcherId);
604         return;
605     }
606 
607     vector<MatchingState> matcherCache(mAllAtomMatchingTrackers.size(),
608                                        MatchingState::kNotComputed);
609     vector<shared_ptr<LogEvent>> matcherTransformations(matcherCache.size(), nullptr);
610 
611     for (const auto& matcherIndex : matchersIt->second) {
612         mAllAtomMatchingTrackers[matcherIndex]->onLogEvent(event, matcherIndex,
613                                                            mAllAtomMatchingTrackers, matcherCache,
614                                                            matcherTransformations);
615     }
616 
617     // Set of metrics that received an activation cancellation.
618     unordered_set<int> metricIndicesWithCanceledActivations;
619 
620     // Determine which metric activations received a cancellation and cancel them.
621     for (const auto& it : mDeactivationAtomTrackerToMetricMap) {
622         if (matcherCache[it.first] == MatchingState::kMatched) {
623             for (int metricIndex : it.second) {
624                 mAllMetricProducers[metricIndex]->cancelEventActivation(it.first);
625                 metricIndicesWithCanceledActivations.insert(metricIndex);
626             }
627         }
628     }
629 
630     // Determine whether any metrics are no longer active after cancelling metric activations.
631     for (const int metricIndex : metricIndicesWithCanceledActivations) {
632         const sp<MetricProducer>& metric = mAllMetricProducers[metricIndex];
633         metric->flushIfExpire(eventTimeNs);
634         if (!metric->isActive()) {
635             activeMetricsIndices.erase(metricIndex);
636         }
637     }
638 
639     isActive |= !activeMetricsIndices.empty();
640 
641     // Determine which metric activations should be turned on and turn them on
642     for (const auto& it : mActivationAtomTrackerToMetricMap) {
643         if (matcherCache[it.first] == MatchingState::kMatched) {
644             for (int metricIndex : it.second) {
645                 mAllMetricProducers[metricIndex]->activate(it.first, eventTimeNs);
646                 isActive |= mAllMetricProducers[metricIndex]->isActive();
647             }
648         }
649     }
650 
651     mIsActive = isActive;
652 
653     // A bitmap to see which ConditionTracker needs to be re-evaluated.
654     vector<uint8_t> conditionToBeEvaluated(mAllConditionTrackers.size(), false);
655     vector<shared_ptr<LogEvent>> conditionToTransformedLogEvents(mAllConditionTrackers.size(),
656                                                                  nullptr);
657 
658     for (const auto& [matcherIndex, conditionList] : mTrackerToConditionMap) {
659         if (matcherCache[matcherIndex] == MatchingState::kMatched) {
660             for (const int conditionIndex : conditionList) {
661                 conditionToBeEvaluated[conditionIndex] = true;
662                 conditionToTransformedLogEvents[conditionIndex] =
663                         matcherTransformations[matcherIndex];
664             }
665         }
666     }
667 
668     vector<ConditionState> conditionCache(mAllConditionTrackers.size(),
669                                           ConditionState::kNotEvaluated);
670     // A bitmap to track if a condition has changed value.
671     vector<uint8_t> changedCache(mAllConditionTrackers.size(), false);
672     for (size_t i = 0; i < mAllConditionTrackers.size(); i++) {
673         if (!conditionToBeEvaluated[i]) {
674             continue;
675         }
676         sp<ConditionTracker>& condition = mAllConditionTrackers[i];
677         const LogEvent& conditionEvent = conditionToTransformedLogEvents[i] == nullptr
678                                                  ? event
679                                                  : *conditionToTransformedLogEvents[i];
680         condition->evaluateCondition(conditionEvent, matcherCache, mAllConditionTrackers,
681                                      conditionCache, changedCache);
682     }
683 
684     for (size_t i = 0; i < mAllConditionTrackers.size(); i++) {
685         if (!changedCache[i]) {
686             continue;
687         }
688         auto it = mConditionToMetricMap.find(i);
689         if (it == mConditionToMetricMap.end()) {
690             continue;
691         }
692         auto& metricList = it->second;
693         for (auto metricIndex : metricList) {
694             // Metric cares about non sliced condition, and it's changed.
695             // Push the new condition to it directly.
696             if (!mAllMetricProducers[metricIndex]->isConditionSliced()) {
697                 mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i],
698                                                                      eventTimeNs);
699                 // Metric cares about sliced conditions, and it may have changed. Send
700                 // notification, and the metric can query the sliced conditions that are
701                 // interesting to it.
702             } else {
703                 mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(conditionCache[i],
704                                                                              eventTimeNs);
705             }
706         }
707     }
708     // For matched AtomMatchers, tell relevant metrics that a matched event has come.
709     for (size_t i = 0; i < mAllAtomMatchingTrackers.size(); i++) {
710         if (matcherCache[i] == MatchingState::kMatched) {
711             StatsdStats::getInstance().noteMatcherMatched(mConfigKey,
712                                                           mAllAtomMatchingTrackers[i]->getId());
713             auto it = mTrackerToMetricMap.find(i);
714             if (it == mTrackerToMetricMap.end()) {
715                 continue;
716             }
717             auto& metricList = it->second;
718             const LogEvent& metricEvent =
719                     matcherTransformations[i] == nullptr ? event : *matcherTransformations[i];
720             for (const int metricIndex : metricList) {
721                 // pushed metrics are never scheduled pulls
722                 mAllMetricProducers[metricIndex]->onMatchedLogEvent(i, metricEvent);
723             }
724         }
725     }
726 }
727 
onLogEventLost(const SocketLossInfo & socketLossInfo)728 void MetricsManager::onLogEventLost(const SocketLossInfo& socketLossInfo) {
729     // socketLossInfo stores atomId per UID - to eliminate duplicates using set
730     const set<int> uniqueLostAtomIds(socketLossInfo.atomIds.begin(), socketLossInfo.atomIds.end());
731 
732     // pass lost atom id to all relevant metrics
733     for (const auto lostAtomId : uniqueLostAtomIds) {
734         /**
735          * Socket loss atom:
736          *  - comes from a specific uid (originUid)
737          *  - specifies the uid in the atom payload (socketLossInfo.uid)
738          *  - provides a list of atom ids that are lost
739          *
740          * For atom id that is lost (lostAtomId below):
741          * - if that atom id is allowed from any uid, then always count this atom as lost
742          * - else, if the originUid (from ucred) (socketLossInfo.uid below and is the same for all
743          *   uniqueLostAtomIds) is in the allowed log sources - count this atom as lost
744          */
745 
746         if (!checkLogCredentials(socketLossInfo.uid, lostAtomId)) {
747             continue;
748         }
749 
750         notifyMetricsAboutLostAtom(lostAtomId, DATA_CORRUPTED_SOCKET_LOSS);
751     }
752 }
753 
notifyMetricsAboutLostAtom(int32_t lostAtomId,DataCorruptedReason reason)754 int MetricsManager::notifyMetricsAboutLostAtom(int32_t lostAtomId, DataCorruptedReason reason) {
755     const auto matchersIt = mTagIdsToMatchersMap.find(lostAtomId);
756     if (matchersIt == mTagIdsToMatchersMap.end()) {
757         // atom is lost - but no metrics in config reference it
758         return 0;
759     }
760     int numberOfNotifiedMetrics = 0;
761 
762     const auto& matchersIndexesListForLostAtom = matchersIt->second;
763     for (const auto matcherIndex : matchersIndexesListForLostAtom) {
764         // look through any metric which depends on matcher
765         auto metricMapIt = mTrackerToMetricMap.find(matcherIndex);
766         if (metricMapIt != mTrackerToMetricMap.end()) {
767             const auto& metricsList = metricMapIt->second;
768             for (const int metricIndex : metricsList) {
769                 mAllMetricProducers[metricIndex]->onMatchedLogEventLost(
770                         lostAtomId, reason, MetricProducer::LostAtomType::kWhat);
771                 numberOfNotifiedMetrics++;
772             }
773         }
774 
775         // look through any condition tracker which depends on matcher
776         const auto conditionMapIt = mTrackerToConditionMap.find(matcherIndex);
777         if (conditionMapIt != mTrackerToConditionMap.end()) {
778             const auto& conditionTrackersList = conditionMapIt->second;
779             for (const int conditionTrackerIndex : conditionTrackersList) {
780                 metricMapIt = mConditionToMetricMap.find(conditionTrackerIndex);
781                 if (metricMapIt != mConditionToMetricMap.end()) {
782                     const auto& metricsList = metricMapIt->second;
783                     for (const int metricIndex : metricsList) {
784                         mAllMetricProducers[metricIndex]->onMatchedLogEventLost(
785                                 lostAtomId, reason, MetricProducer::LostAtomType::kCondition);
786                         numberOfNotifiedMetrics++;
787                     }
788                 }
789             }
790         }
791     }
792     return numberOfNotifiedMetrics;
793 }
794 
onAnomalyAlarmFired(const int64_t timestampNs,unordered_set<sp<const InternalAlarm>,SpHash<InternalAlarm>> & alarmSet)795 void MetricsManager::onAnomalyAlarmFired(
796         const int64_t timestampNs,
797         unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) {
798     for (const auto& itr : mAllAnomalyTrackers) {
799         itr->informAlarmsFired(timestampNs, alarmSet);
800     }
801 }
802 
onPeriodicAlarmFired(const int64_t timestampNs,unordered_set<sp<const InternalAlarm>,SpHash<InternalAlarm>> & alarmSet)803 void MetricsManager::onPeriodicAlarmFired(
804         const int64_t timestampNs,
805         unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) {
806     for (const auto& itr : mAllPeriodicAlarmTrackers) {
807         itr->informAlarmsFired(timestampNs, alarmSet);
808     }
809 }
810 
811 // Returns the total byte size of all metrics managed by a single config source.
byteSize()812 size_t MetricsManager::byteSize() {
813     size_t totalSize = 0;
814     for (const auto& metricProducer : mAllMetricProducers) {
815         totalSize += metricProducer->byteSize();
816     }
817     return totalSize;
818 }
819 
loadActiveConfig(const ActiveConfig & config,int64_t currentTimeNs)820 void MetricsManager::loadActiveConfig(const ActiveConfig& config, int64_t currentTimeNs) {
821     if (config.metric_size() == 0) {
822         ALOGW("No active metric for config %s", mConfigKey.ToString().c_str());
823         return;
824     }
825 
826     for (int i = 0; i < config.metric_size(); i++) {
827         const auto& activeMetric = config.metric(i);
828         for (int metricIndex : mMetricIndexesWithActivation) {
829             const auto& metric = mAllMetricProducers[metricIndex];
830             if (metric->getMetricId() == activeMetric.id()) {
831                 VLOG("Setting active metric: %lld", (long long)metric->getMetricId());
832                 metric->loadActiveMetric(activeMetric, currentTimeNs);
833                 if (!mIsActive && metric->isActive()) {
834                     StatsdStats::getInstance().noteActiveStatusChanged(mConfigKey,
835                                                                        /*activate=*/true);
836                 }
837                 mIsActive |= metric->isActive();
838             }
839         }
840     }
841 }
842 
writeActiveConfigToProtoOutputStream(int64_t currentTimeNs,const DumpReportReason reason,ProtoOutputStream * proto)843 void MetricsManager::writeActiveConfigToProtoOutputStream(int64_t currentTimeNs,
844                                                           const DumpReportReason reason,
845                                                           ProtoOutputStream* proto) {
846     proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_CONFIG_ID, (long long)mConfigKey.GetId());
847     proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_CONFIG_UID, mConfigKey.GetUid());
848     for (int metricIndex : mMetricIndexesWithActivation) {
849         const auto& metric = mAllMetricProducers[metricIndex];
850         const uint64_t metricToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
851                                                   FIELD_ID_ACTIVE_CONFIG_METRIC);
852         metric->writeActiveMetricToProtoOutputStream(currentTimeNs, reason, proto);
853         proto->end(metricToken);
854     }
855 }
856 
writeMetadataToProto(int64_t currentWallClockTimeNs,int64_t systemElapsedTimeNs,metadata::StatsMetadata * statsMetadata)857 bool MetricsManager::writeMetadataToProto(int64_t currentWallClockTimeNs,
858                                           int64_t systemElapsedTimeNs,
859                                           metadata::StatsMetadata* statsMetadata) {
860     bool metadataWritten = false;
861     metadata::ConfigKey* configKey = statsMetadata->mutable_config_key();
862     configKey->set_config_id(mConfigKey.GetId());
863     configKey->set_uid(mConfigKey.GetUid());
864     for (const auto& anomalyTracker : mAllAnomalyTrackers) {
865         metadata::AlertMetadata* alertMetadata = statsMetadata->add_alert_metadata();
866         bool alertWritten = anomalyTracker->writeAlertMetadataToProto(
867                 currentWallClockTimeNs, systemElapsedTimeNs, alertMetadata);
868         if (!alertWritten) {
869             statsMetadata->mutable_alert_metadata()->RemoveLast();
870         }
871         metadataWritten |= alertWritten;
872     }
873 
874     for (const auto& metricProducer : mAllMetricProducers) {
875         metadata::MetricMetadata* metricMetadata = statsMetadata->add_metric_metadata();
876         bool metricWritten = metricProducer->writeMetricMetadataToProto(metricMetadata);
877         if (!metricWritten) {
878             statsMetadata->mutable_metric_metadata()->RemoveLast();
879         }
880         metadataWritten |= metricWritten;
881     }
882     return metadataWritten;
883 }
884 
loadMetadata(const metadata::StatsMetadata & metadata,int64_t currentWallClockTimeNs,int64_t systemElapsedTimeNs)885 void MetricsManager::loadMetadata(const metadata::StatsMetadata& metadata,
886                                   int64_t currentWallClockTimeNs, int64_t systemElapsedTimeNs) {
887     for (const metadata::AlertMetadata& alertMetadata : metadata.alert_metadata()) {
888         int64_t alertId = alertMetadata.alert_id();
889         const auto& it = mAlertTrackerMap.find(alertId);
890         if (it == mAlertTrackerMap.end()) {
891             ALOGE("No anomalyTracker found for alertId %lld", (long long)alertId);
892             continue;
893         }
894         mAllAnomalyTrackers[it->second]->loadAlertMetadata(alertMetadata, currentWallClockTimeNs,
895                                                            systemElapsedTimeNs);
896     }
897     for (const metadata::MetricMetadata& metricMetadata : metadata.metric_metadata()) {
898         int64_t metricId = metricMetadata.metric_id();
899         const auto& it = mMetricProducerMap.find(metricId);
900         if (it == mMetricProducerMap.end()) {
901             ALOGE("No metricProducer found for metricId %lld", (long long)metricId);
902         }
903         mAllMetricProducers[it->second]->loadMetricMetadataFromProto(metricMetadata);
904     }
905 }
906 
enforceRestrictedDataTtls(const int64_t wallClockNs)907 void MetricsManager::enforceRestrictedDataTtls(const int64_t wallClockNs) {
908     if (!hasRestrictedMetricsDelegate()) {
909         return;
910     }
911     sqlite3* db = dbutils::getDb(mConfigKey);
912     if (db == nullptr) {
913         ALOGE("Failed to open sqlite db");
914         dbutils::closeDb(db);
915         return;
916     }
917     for (const auto& producer : mAllMetricProducers) {
918         producer->enforceRestrictedDataTtl(db, wallClockNs);
919     }
920     dbutils::closeDb(db);
921 }
922 
validateRestrictedMetricsDelegate(const int32_t callingUid)923 bool MetricsManager::validateRestrictedMetricsDelegate(const int32_t callingUid) {
924     if (!hasRestrictedMetricsDelegate()) {
925         return false;
926     }
927 
928     set<int32_t> possibleUids = mUidMap->getAppUid(mRestrictedMetricsDelegatePackageName.value());
929 
930     return possibleUids.find(callingUid) != possibleUids.end();
931 }
932 
flushRestrictedData()933 void MetricsManager::flushRestrictedData() {
934     if (!hasRestrictedMetricsDelegate()) {
935         return;
936     }
937     int64_t flushStartNs = getElapsedRealtimeNs();
938     for (const auto& producer : mAllMetricProducers) {
939         producer->flushRestrictedData();
940     }
941     StatsdStats::getInstance().noteRestrictedConfigFlushLatency(
942             mConfigKey, getElapsedRealtimeNs() - flushStartNs);
943 }
944 
getAllMetricIds() const945 vector<int64_t> MetricsManager::getAllMetricIds() const {
946     vector<int64_t> metricIds;
947     metricIds.reserve(mMetricProducerMap.size());
948     for (const auto& [metricId, _] : mMetricProducerMap) {
949         metricIds.push_back(metricId);
950     }
951     return metricIds;
952 }
953 
addAllAtomIds(LogEventFilter::AtomIdSet & allIds) const954 void MetricsManager::addAllAtomIds(LogEventFilter::AtomIdSet& allIds) const {
955     for (const auto& [atomId, _] : mTagIdsToMatchersMap) {
956         allIds.insert(atomId);
957     }
958 }
959 
processQueueOverflowStats(const StatsdStats::QueueOverflowAtomsStats & overflowStats)960 void MetricsManager::processQueueOverflowStats(
961         const StatsdStats::QueueOverflowAtomsStats& overflowStats) {
962     assert((overflowStats.size() < mQueueOverflowAtomsStats.size()) &&
963            "StatsdStats reset unexpected");
964 
965     for (const auto [atomId, count] : overflowStats) {
966         // are there new atoms dropped due to queue overflow since previous dumpReport request
967         auto droppedAtomStatsIt = mQueueOverflowAtomsStats.find(atomId);
968         if (droppedAtomStatsIt != mQueueOverflowAtomsStats.end() &&
969             droppedAtomStatsIt->second == count) {
970             // no new dropped atoms detected for the atomId
971             continue;
972         }
973 
974         if (notifyMetricsAboutLostAtom(atomId, DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW) > 0) {
975             // there is at least one metric interested in the lost atom, keep track of it
976             // to update it again only if there will be more dropped atoms
977             mQueueOverflowAtomsStats[atomId] = count;
978         } else {
979             // there are no metrics interested in dropped atom
980             if (droppedAtomStatsIt != mQueueOverflowAtomsStats.end()) {
981                 // but there were metrics which are interested in the atom and now they are removed
982                 mQueueOverflowAtomsStats.erase(droppedAtomStatsIt);
983             }
984         }
985     }
986 }
987 
988 }  // namespace statsd
989 }  // namespace os
990 }  // namespace android
991